Skip to content

Commit

Permalink
fest(std/node): implement os.getPriority() and os.setPriority()
Browse files Browse the repository at this point in the history
  • Loading branch information
ecyrbe committed Mar 1, 2020
1 parent ba0991a commit f8994cb
Show file tree
Hide file tree
Showing 11 changed files with 289 additions and 20 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ clap = "2.33.0"
dirs = "2.0.2"
dlopen = "0.1.8"
dprint-plugin-typescript = "0.7.0"
errno = "0.1.8"
futures = { version = "0.3.1", features = [ "compat", "io-compat" ] }
glob = "0.3.0"
http = "0.2.0"
Expand Down
2 changes: 2 additions & 0 deletions cli/js/deno.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ export {
execPath,
hostname,
loadavg,
getPriority,
setPriority,
osRelease
} from "./os.ts";
export {
Expand Down
6 changes: 6 additions & 0 deletions cli/js/lib.deno.ns.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ declare namespace Deno {
/** Exit the Deno process with optional exit code. */
export function exit(code?: number): never;

/** Get process priority */
export function getPriority(pid?: number): number;

/** Set process priority */
export function setPriority(priority: number, pid?: number): void;

/** Returns a snapshot of the environment variables at invocation. Mutating a
* property in the object will set that variable in the environment for the
* process. The environment object will only accept `string`s as values.
Expand Down
10 changes: 10 additions & 0 deletions cli/js/os.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@ export function exit(code = 0): never {
return util.unreachable();
}

/** Get process priority */
export function getPriority(pid = 0): number {
return sendSync("op_get_priority", { pid });
}

/** Set process priority */
export function setPriority(priority: number, pid = 0): void {
sendSync("op_set_priority", { pid, priority });
}

function setEnv(key: string, value: string): void {
sendSync("op_set_env", { key, value });
}
Expand Down
5 changes: 5 additions & 0 deletions cli/js/os_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ test(function osPid(): void {
assert(Deno.pid > 0);
});

test(function testGetPriority(): void {
const priority = Deno.getPriority();
console.log("priority", priority);
});

testPerm({ env: true }, function getDir(): void {
type supportOS = "mac" | "win" | "linux";

Expand Down
1 change: 1 addition & 0 deletions cli/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ pub mod msg;
pub mod op_error;
pub mod ops;
pub mod permissions;
pub mod priority;
mod repl;
pub mod resolve_addr;
pub mod signal;
Expand Down
34 changes: 34 additions & 0 deletions cli/ops/os.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
use super::dispatch_json::{Deserialize, JsonOp, Value};
use crate::op_error::OpError;
use crate::priority::{get_priority, set_priority};
use crate::state::State;
use deno_core::*;
use std::collections::HashMap;
Expand All @@ -19,6 +20,8 @@ pub fn init(i: &mut Isolate, s: &State) {
i.register_op("op_hostname", s.stateful_json_op(op_hostname));
i.register_op("op_loadavg", s.stateful_json_op(op_loadavg));
i.register_op("op_os_release", s.stateful_json_op(op_os_release));
i.register_op("op_get_priority", s.stateful_json_op(op_get_priority));
i.register_op("op_set_priority", s.stateful_json_op(op_set_priority));
}

#[derive(Deserialize)]
Expand Down Expand Up @@ -184,3 +187,34 @@ fn op_os_release(
let release = sys_info::os_release().unwrap_or_else(|_| "".to_string());
Ok(JsonOp::Sync(json!(release)))
}

#[derive(Deserialize)]
struct GetPriorityArgs {
pid: u32,
}

fn op_get_priority(
_state: &State,
args: Value,
_zero_copy: Option<ZeroCopyBuf>,
) -> Result<JsonOp, OpError> {
let args: GetPriorityArgs = serde_json::from_value(args)?;
let priority = get_priority(args.pid)?;
Ok(JsonOp::Sync(json!(priority)))
}

#[derive(Deserialize)]
struct SetPriorityArgs {
pid: u32,
priority: i32,
}

fn op_set_priority(
_state: &State,
args: Value,
_zero_copy: Option<ZeroCopyBuf>,
) -> Result<JsonOp, OpError> {
let args: SetPriorityArgs = serde_json::from_value(args)?;
set_priority(args.pid, args.priority)?;
Ok(JsonOp::Sync(json!({})))
}
159 changes: 159 additions & 0 deletions cli/priority.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
use crate::op_error::OpError;

#[cfg(unix)]
use errno::{errno, set_errno, Errno};
#[cfg(unix)]
use libc::{id_t, PRIO_PROCESS};
#[cfg(windows)]
use winapi::shared::minwindef::{DWORD, FALSE};
#[cfg(windows)]
use winapi::shared::ntdef::NULL;
#[cfg(windows)]
use winapi::um::handleapi::CloseHandle;
#[cfg(windows)]
use winapi::um::processthreadsapi::{
GetCurrentProcess, GetPriorityClass, OpenProcess, SetPriorityClass,
};
#[cfg(windows)]
use winapi::um::winbase::{
ABOVE_NORMAL_PRIORITY_CLASS, BELOW_NORMAL_PRIORITY_CLASS,
HIGH_PRIORITY_CLASS, IDLE_PRIORITY_CLASS, NORMAL_PRIORITY_CLASS,
REALTIME_PRIORITY_CLASS,
};
#[cfg(windows)]
use winapi::um::winnt::PROCESS_QUERY_LIMITED_INFORMATION;
#[cfg(target_os = "macos")]
#[allow(non_camel_case_types)]
type priority_t = i32;
#[cfg(target_os = "linux")]
#[allow(non_camel_case_types)]
type priority_t = u32;

pub const PRIORITY_LOW: i32 = 19;
pub const PRIORITY_BELOW_NORMAL: i32 = 10;
pub const PRIORITY_NORMAL: i32 = 0;
pub const PRIORITY_ABOVE_NORMAL: i32 = -7;
pub const PRIORITY_HIGH: i32 = -14;
pub const PRIORITY_HIGHEST: i32 = -20;

#[cfg(unix)]
pub fn get_priority(pid: u32) -> Result<i32, OpError> {
unsafe {
set_errno(Errno(0));
match (
libc::getpriority(PRIO_PROCESS as priority_t, pid as id_t),
errno(),
) {
(-1, Errno(0)) => Ok(PRIORITY_HIGH),
(-1, _) => Err(OpError::from(std::io::Error::last_os_error())),
(priority, _) => Ok(priority),
}
}
}

#[cfg(unix)]
pub fn set_priority(pid: u32, priority: i32) -> Result<(), OpError> {
unsafe {
match libc::setpriority(PRIO_PROCESS as priority_t, pid as id_t, priority) {
-1 => Err(OpError::from(std::io::Error::last_os_error())),
_ => Ok(()),
}
}
}

#[cfg(windows)]
pub fn get_priority(pid: u32) -> Result<i32, OpError> {
unsafe {
let handle = if pid == 0 {
GetCurrentProcess()
} else {
OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid as DWORD)
};
if handle == NULL {
Err(OpError::from(std::io::Error::last_os_error()))
} else {
let result = match GetPriorityClass(handle) {
0 => Err(OpError::from(std::io::Error::last_os_error())),
REALTIME_PRIORITY_CLASS => Ok(PRIORITY_HIGHEST),
HIGH_PRIORITY_CLASS => Ok(PRIORITY_HIGH),
ABOVE_NORMAL_PRIORITY_CLASS => Ok(PRIORITY_ABOVE_NORMAL),
NORMAL_PRIORITY_CLASS => Ok(PRIORITY_NORMAL),
BELOW_NORMAL_PRIORITY_CLASS => Ok(PRIORITY_BELOW_NORMAL),
IDLE_PRIORITY_CLASS => Ok(PRIORITY_LOW),
_ => Ok(PRIORITY_LOW),
};
CloseHandle(handle);
result
}
}
}

#[cfg(windows)]
pub fn set_priority(pid: u32, priority: i32) -> Result<(), OpError> {
unsafe {
let handle = if pid == 0 {
GetCurrentProcess()
} else {
OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid as DWORD)
};
if handle == NULL {
Err(OpError::from(std::io::Error::last_os_error()))
} else {
let prio_class = match priority {
p if p <= PRIORITY_HIGHEST => REALTIME_PRIORITY_CLASS,
p if PRIORITY_HIGHEST < p && p <= PRIORITY_HIGH => HIGH_PRIORITY_CLASS,
p if PRIORITY_HIGH < p && p <= PRIORITY_ABOVE_NORMAL => {
ABOVE_NORMAL_PRIORITY_CLASS
}
p if PRIORITY_ABOVE_NORMAL < p && p <= PRIORITY_NORMAL => {
NORMAL_PRIORITY_CLASS
}
p if PRIORITY_NORMAL < p && p <= PRIORITY_BELOW_NORMAL => {
BELOW_NORMAL_PRIORITY_CLASS
}
_ => IDLE_PRIORITY_CLASS,
};
let result = match SetPriorityClass(handle, prio_class) {
FALSE => Err(OpError::from(std::io::Error::last_os_error())),
_ => Ok(()),
};
CloseHandle(handle);
result
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_get_current_process_priority() {
get_priority(0).expect("Should get priority");
}

#[cfg(unix)]
#[test]
fn test_set_current_process_high_priority_should_fail() {
assert!(set_priority(0, PRIORITY_HIGH).is_err());
}

/// this test makes multiple tests at once
/// because we need to set them in order and rust
/// does not garanty test order execution
#[test]
fn test_set_current_process_priority_from_normal_to_low() {
set_priority(0, PRIORITY_NORMAL).expect("Should set priority");
let priority = get_priority(0).expect("Should get priority");
assert_eq!(priority, PRIORITY_NORMAL);

set_priority(0, PRIORITY_BELOW_NORMAL).expect("Should set priority");
let priority = get_priority(0).expect("Should get priority");
assert_eq!(priority, PRIORITY_BELOW_NORMAL);

set_priority(0, PRIORITY_LOW).expect("Should set priority");
let priority = get_priority(0).expect("Should get priority");
assert_eq!(priority, PRIORITY_LOW);
}
}
24 changes: 18 additions & 6 deletions std/node/os.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,13 @@ totalmem[Symbol.toPrimitive] = (): number => totalmem();
type[Symbol.toPrimitive] = (): string => type();
uptime[Symbol.toPrimitive] = (): number => uptime();

const PRIORITY_LOW = 19;
const PRIORITY_BELOW_NORMAL = 10;
const PRIORITY_NORMAL = 0;
const PRIORITY_ABOVE_NORMAL = -7;
const PRIORITY_HIGH = -14;
const PRIORITY_HIGHEST = -20;

/** Returns the operating system CPU architecture for which the Deno binary was compiled */
export function arch(): string {
return Deno.build.arch;
Expand Down Expand Up @@ -128,10 +135,10 @@ export function freemem(): number {
notImplemented(SEE_GITHUB_ISSUE);
}

/** Not yet implemented */
/** Get process priority */
export function getPriority(pid = 0): number {
validateIntegerRange(pid, "pid");
notImplemented(SEE_GITHUB_ISSUE);
return Deno.getPriority(pid);
}

/** Returns the string path of the current user's home directory. */
Expand Down Expand Up @@ -166,7 +173,7 @@ export function release(): string {
return Deno.osRelease();
}

/** Not yet implemented */
/** Set process priority */
export function setPriority(pid: number, priority?: number): void {
/* The node API has the 'pid' as the first parameter and as optional.
This makes for a problematic implementation in Typescript. */
Expand All @@ -175,9 +182,8 @@ export function setPriority(pid: number, priority?: number): void {
pid = 0;
}
validateIntegerRange(pid, "pid");
validateIntegerRange(priority, "priority", -20, 19);

notImplemented(SEE_GITHUB_ISSUE);
validateIntegerRange(priority, "priority", PRIORITY_HIGHEST, PRIORITY_LOW);
Deno.setPriority(priority, pid);
}

/** Not yet implemented */
Expand Down Expand Up @@ -219,6 +225,12 @@ export const constants = {
signals: Deno.Signal,
priority: {
// see https://nodejs.org/docs/latest-v12.x/api/os.html#os_priority_constants
PRIORITY_LOW,
PRIORITY_BELOW_NORMAL,
PRIORITY_NORMAL,
PRIORITY_ABOVE_NORMAL,
PRIORITY_HIGH,
PRIORITY_HIGHEST
}
};

Expand Down
Loading

0 comments on commit f8994cb

Please sign in to comment.