Compare commits
No commits in common. 'master' and 'gh-pages' have entirely different histories.
@ -1,23 +0,0 @@
|
|||||||
name: github pages
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
deploy:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- name: Setup mdBook
|
|
||||||
uses: peaceiris/actions-mdbook@v1
|
|
||||||
with:
|
|
||||||
mdbook-version: 'latest'
|
|
||||||
- name: Build mdbook
|
|
||||||
run: mdbook build docs
|
|
||||||
- name: Deploy
|
|
||||||
uses: peaceiris/actions-gh-pages@v3
|
|
||||||
with:
|
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
publish_dir: ./docs/book
|
|
||||||
@ -1,143 +0,0 @@
|
|||||||
name: CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
check:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
profile: minimal
|
|
||||||
toolchain: nightly-2021-07-27
|
|
||||||
override: true
|
|
||||||
components: rustfmt, clippy
|
|
||||||
- name: Check code format
|
|
||||||
run: |
|
|
||||||
cd code
|
|
||||||
cargo fmt --all -- --check
|
|
||||||
cd ch02-03
|
|
||||||
cargo fmt --all -- --check
|
|
||||||
cd ../ch03-02
|
|
||||||
cargo fmt --all -- --check
|
|
||||||
cd ../ch03-04
|
|
||||||
cargo fmt --all -- --check
|
|
||||||
cd ../ch04-01
|
|
||||||
cargo fmt --all -- --check
|
|
||||||
cd ../ch04-03
|
|
||||||
cargo fmt --all -- --check
|
|
||||||
# uses: actions-rs/cargo@v1
|
|
||||||
# with:
|
|
||||||
# command: fmt
|
|
||||||
# args: --all -- --check
|
|
||||||
- name: Clippy
|
|
||||||
run: |
|
|
||||||
cd code
|
|
||||||
cargo clippy
|
|
||||||
cd ch02-03
|
|
||||||
cargo clippy
|
|
||||||
cd ../ch03-02
|
|
||||||
cargo clippy
|
|
||||||
cd ../ch03-04
|
|
||||||
cargo clippy
|
|
||||||
cd ../ch04-01
|
|
||||||
cargo clippy
|
|
||||||
cd ../ch04-03
|
|
||||||
cargo clippy
|
|
||||||
# uses: actions-rs/cargo@v1
|
|
||||||
# with:
|
|
||||||
# command: clippy
|
|
||||||
build:
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os: [ubuntu-20.04]
|
|
||||||
# os: [ubuntu-20.04, macos-latest]
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
submodules: 'recursive'
|
|
||||||
- uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
profile: minimal
|
|
||||||
toolchain: nightly-2021-07-27
|
|
||||||
components: rust-src
|
|
||||||
- name: Build
|
|
||||||
run: |
|
|
||||||
cd code
|
|
||||||
cargo build
|
|
||||||
cd ch02-03
|
|
||||||
cargo build
|
|
||||||
cd ../ch03-02
|
|
||||||
cargo build
|
|
||||||
cd ../ch03-04
|
|
||||||
cargo build
|
|
||||||
cd ../ch04-01
|
|
||||||
cargo build
|
|
||||||
cd ../ch04-03
|
|
||||||
cargo build
|
|
||||||
# uses: actions-rs/cargo@v1
|
|
||||||
# with:
|
|
||||||
# command: build
|
|
||||||
test:
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os: [ubuntu-20.04]
|
|
||||||
# os: [ubuntu-20.04, macos-latest]
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
submodules: 'recursive'
|
|
||||||
- uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
profile: minimal
|
|
||||||
toolchain: nightly-2021-07-27
|
|
||||||
components: rust-src
|
|
||||||
- name: Test
|
|
||||||
run: |
|
|
||||||
cd code
|
|
||||||
cargo test
|
|
||||||
cd ch02-03
|
|
||||||
cargo test
|
|
||||||
cd ../ch03-02
|
|
||||||
cargo test
|
|
||||||
cd ../ch03-04
|
|
||||||
cargo test
|
|
||||||
cd ../ch04-01
|
|
||||||
cargo test
|
|
||||||
cd ../ch04-03
|
|
||||||
cargo test
|
|
||||||
doc:
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os: [ubuntu-20.04]
|
|
||||||
# os: [ubuntu-20.04, macos-latest]
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
submodules: 'recursive'
|
|
||||||
- uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
profile: minimal
|
|
||||||
toolchain: nightly-2021-07-27
|
|
||||||
components: rust-src
|
|
||||||
- name: Build docs
|
|
||||||
run: |
|
|
||||||
cd code
|
|
||||||
cargo doc --no-deps --all-features
|
|
||||||
cd ch02-03
|
|
||||||
cargo doc --no-deps --all-features
|
|
||||||
cd ../ch03-02
|
|
||||||
cargo doc --no-deps --all-features
|
|
||||||
cd ../ch03-04
|
|
||||||
cargo doc --no-deps --all-features
|
|
||||||
cd ../ch04-01
|
|
||||||
cargo doc --no-deps --all-features
|
|
||||||
cd ../ch04-03
|
|
||||||
cargo doc --no-deps --all-features
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
## Editor
|
|
||||||
*.swp
|
|
||||||
*.swo
|
|
||||||
Session.vim
|
|
||||||
.cproject
|
|
||||||
.idea
|
|
||||||
*.iml
|
|
||||||
.vscode
|
|
||||||
.project
|
|
||||||
.favorites.json
|
|
||||||
.settings/
|
|
||||||
# Generated by Cargo
|
|
||||||
# will have compiled files and executables
|
|
||||||
debug/
|
|
||||||
target/
|
|
||||||
|
|
||||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
|
||||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
|
||||||
Cargo.lock
|
|
||||||
|
|
||||||
# These are backup files generated by rustfmt
|
|
||||||
**/*.rs.bk
|
|
||||||
|
|
||||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
|
||||||
*.pdb
|
|
||||||
|
|
||||||
## File system
|
|
||||||
.DS_Store
|
|
||||||
desktop.ini
|
|
||||||
@ -0,0 +1 @@
|
|||||||
|
This file makes sure that Github Pages doesn't process mdBook's output.
|
||||||
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 434 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
Based off of the Ayu theme
|
||||||
|
Original by Dempfi (https://github.com/dempfi/ayu)
|
||||||
|
*/
|
||||||
|
|
||||||
|
.hljs {
|
||||||
|
display: block;
|
||||||
|
overflow-x: auto;
|
||||||
|
background: #191f26;
|
||||||
|
color: #e6e1cf;
|
||||||
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-comment,
|
||||||
|
.hljs-quote {
|
||||||
|
color: #5c6773;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-variable,
|
||||||
|
.hljs-template-variable,
|
||||||
|
.hljs-attribute,
|
||||||
|
.hljs-attr,
|
||||||
|
.hljs-regexp,
|
||||||
|
.hljs-link,
|
||||||
|
.hljs-selector-id,
|
||||||
|
.hljs-selector-class {
|
||||||
|
color: #ff7733;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-number,
|
||||||
|
.hljs-meta,
|
||||||
|
.hljs-builtin-name,
|
||||||
|
.hljs-literal,
|
||||||
|
.hljs-type,
|
||||||
|
.hljs-params {
|
||||||
|
color: #ffee99;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-string,
|
||||||
|
.hljs-bullet {
|
||||||
|
color: #b8cc52;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-title,
|
||||||
|
.hljs-built_in,
|
||||||
|
.hljs-section {
|
||||||
|
color: #ffb454;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-keyword,
|
||||||
|
.hljs-selector-tag,
|
||||||
|
.hljs-symbol {
|
||||||
|
color: #ff7733;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-name {
|
||||||
|
color: #36a3d9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-tag {
|
||||||
|
color: #00568d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-emphasis {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-strong {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-addition {
|
||||||
|
color: #91b362;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-deletion {
|
||||||
|
color: #d96c75;
|
||||||
|
}
|
||||||
@ -0,0 +1,672 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Fix back button cache problem
|
||||||
|
window.onunload = function () { };
|
||||||
|
|
||||||
|
// Global variable, shared between modules
|
||||||
|
function playground_text(playground) {
|
||||||
|
let code_block = playground.querySelector("code");
|
||||||
|
|
||||||
|
if (window.ace && code_block.classList.contains("editable")) {
|
||||||
|
let editor = window.ace.edit(code_block);
|
||||||
|
return editor.getValue();
|
||||||
|
} else {
|
||||||
|
return code_block.textContent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(function codeSnippets() {
|
||||||
|
function fetch_with_timeout(url, options, timeout = 6000) {
|
||||||
|
return Promise.race([
|
||||||
|
fetch(url, options),
|
||||||
|
new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeout))
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var playgrounds = Array.from(document.querySelectorAll(".playground"));
|
||||||
|
if (playgrounds.length > 0) {
|
||||||
|
fetch_with_timeout("https://play.rust-lang.org/meta/crates", {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': "application/json",
|
||||||
|
},
|
||||||
|
method: 'POST',
|
||||||
|
mode: 'cors',
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(response => {
|
||||||
|
// get list of crates available in the rust playground
|
||||||
|
let playground_crates = response.crates.map(item => item["id"]);
|
||||||
|
playgrounds.forEach(block => handle_crate_list_update(block, playground_crates));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handle_crate_list_update(playground_block, playground_crates) {
|
||||||
|
// update the play buttons after receiving the response
|
||||||
|
update_play_button(playground_block, playground_crates);
|
||||||
|
|
||||||
|
// and install on change listener to dynamically update ACE editors
|
||||||
|
if (window.ace) {
|
||||||
|
let code_block = playground_block.querySelector("code");
|
||||||
|
if (code_block.classList.contains("editable")) {
|
||||||
|
let editor = window.ace.edit(code_block);
|
||||||
|
editor.addEventListener("change", function (e) {
|
||||||
|
update_play_button(playground_block, playground_crates);
|
||||||
|
});
|
||||||
|
// add Ctrl-Enter command to execute rust code
|
||||||
|
editor.commands.addCommand({
|
||||||
|
name: "run",
|
||||||
|
bindKey: {
|
||||||
|
win: "Ctrl-Enter",
|
||||||
|
mac: "Ctrl-Enter"
|
||||||
|
},
|
||||||
|
exec: _editor => run_rust_code(playground_block)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// updates the visibility of play button based on `no_run` class and
|
||||||
|
// used crates vs ones available on http://play.rust-lang.org
|
||||||
|
function update_play_button(pre_block, playground_crates) {
|
||||||
|
var play_button = pre_block.querySelector(".play-button");
|
||||||
|
|
||||||
|
// skip if code is `no_run`
|
||||||
|
if (pre_block.querySelector('code').classList.contains("no_run")) {
|
||||||
|
play_button.classList.add("hidden");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get list of `extern crate`'s from snippet
|
||||||
|
var txt = playground_text(pre_block);
|
||||||
|
var re = /extern\s+crate\s+([a-zA-Z_0-9]+)\s*;/g;
|
||||||
|
var snippet_crates = [];
|
||||||
|
var item;
|
||||||
|
while (item = re.exec(txt)) {
|
||||||
|
snippet_crates.push(item[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if all used crates are available on play.rust-lang.org
|
||||||
|
var all_available = snippet_crates.every(function (elem) {
|
||||||
|
return playground_crates.indexOf(elem) > -1;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (all_available) {
|
||||||
|
play_button.classList.remove("hidden");
|
||||||
|
} else {
|
||||||
|
play_button.classList.add("hidden");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function run_rust_code(code_block) {
|
||||||
|
var result_block = code_block.querySelector(".result");
|
||||||
|
if (!result_block) {
|
||||||
|
result_block = document.createElement('code');
|
||||||
|
result_block.className = 'result hljs language-bash';
|
||||||
|
|
||||||
|
code_block.append(result_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
let text = playground_text(code_block);
|
||||||
|
let classes = code_block.querySelector('code').classList;
|
||||||
|
let edition = "2015";
|
||||||
|
if(classes.contains("edition2018")) {
|
||||||
|
edition = "2018";
|
||||||
|
} else if(classes.contains("edition2021")) {
|
||||||
|
edition = "2021";
|
||||||
|
}
|
||||||
|
var params = {
|
||||||
|
version: "stable",
|
||||||
|
optimize: "0",
|
||||||
|
code: text,
|
||||||
|
edition: edition
|
||||||
|
};
|
||||||
|
|
||||||
|
if (text.indexOf("#![feature") !== -1) {
|
||||||
|
params.version = "nightly";
|
||||||
|
}
|
||||||
|
|
||||||
|
result_block.innerText = "Running...";
|
||||||
|
|
||||||
|
fetch_with_timeout("https://play.rust-lang.org/evaluate.json", {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': "application/json",
|
||||||
|
},
|
||||||
|
method: 'POST',
|
||||||
|
mode: 'cors',
|
||||||
|
body: JSON.stringify(params)
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(response => {
|
||||||
|
if (response.result.trim() === '') {
|
||||||
|
result_block.innerText = "No output";
|
||||||
|
result_block.classList.add("result-no-output");
|
||||||
|
} else {
|
||||||
|
result_block.innerText = response.result;
|
||||||
|
result_block.classList.remove("result-no-output");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => result_block.innerText = "Playground Communication: " + error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Syntax highlighting Configuration
|
||||||
|
hljs.configure({
|
||||||
|
tabReplace: ' ', // 4 spaces
|
||||||
|
languages: [], // Languages used for auto-detection
|
||||||
|
});
|
||||||
|
|
||||||
|
let code_nodes = Array
|
||||||
|
.from(document.querySelectorAll('code'))
|
||||||
|
// Don't highlight `inline code` blocks in headers.
|
||||||
|
.filter(function (node) {return !node.parentElement.classList.contains("header"); });
|
||||||
|
|
||||||
|
if (window.ace) {
|
||||||
|
// language-rust class needs to be removed for editable
|
||||||
|
// blocks or highlightjs will capture events
|
||||||
|
code_nodes
|
||||||
|
.filter(function (node) {return node.classList.contains("editable"); })
|
||||||
|
.forEach(function (block) { block.classList.remove('language-rust'); });
|
||||||
|
|
||||||
|
Array
|
||||||
|
code_nodes
|
||||||
|
.filter(function (node) {return !node.classList.contains("editable"); })
|
||||||
|
.forEach(function (block) { hljs.highlightBlock(block); });
|
||||||
|
} else {
|
||||||
|
code_nodes.forEach(function (block) { hljs.highlightBlock(block); });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adding the hljs class gives code blocks the color css
|
||||||
|
// even if highlighting doesn't apply
|
||||||
|
code_nodes.forEach(function (block) { block.classList.add('hljs'); });
|
||||||
|
|
||||||
|
Array.from(document.querySelectorAll("code.language-rust")).forEach(function (block) {
|
||||||
|
|
||||||
|
var lines = Array.from(block.querySelectorAll('.boring'));
|
||||||
|
// If no lines were hidden, return
|
||||||
|
if (!lines.length) { return; }
|
||||||
|
block.classList.add("hide-boring");
|
||||||
|
|
||||||
|
var buttons = document.createElement('div');
|
||||||
|
buttons.className = 'buttons';
|
||||||
|
buttons.innerHTML = "<button class=\"fa fa-eye\" title=\"Show hidden lines\" aria-label=\"Show hidden lines\"></button>";
|
||||||
|
|
||||||
|
// add expand button
|
||||||
|
var pre_block = block.parentNode;
|
||||||
|
pre_block.insertBefore(buttons, pre_block.firstChild);
|
||||||
|
|
||||||
|
pre_block.querySelector('.buttons').addEventListener('click', function (e) {
|
||||||
|
if (e.target.classList.contains('fa-eye')) {
|
||||||
|
e.target.classList.remove('fa-eye');
|
||||||
|
e.target.classList.add('fa-eye-slash');
|
||||||
|
e.target.title = 'Hide lines';
|
||||||
|
e.target.setAttribute('aria-label', e.target.title);
|
||||||
|
|
||||||
|
block.classList.remove('hide-boring');
|
||||||
|
} else if (e.target.classList.contains('fa-eye-slash')) {
|
||||||
|
e.target.classList.remove('fa-eye-slash');
|
||||||
|
e.target.classList.add('fa-eye');
|
||||||
|
e.target.title = 'Show hidden lines';
|
||||||
|
e.target.setAttribute('aria-label', e.target.title);
|
||||||
|
|
||||||
|
block.classList.add('hide-boring');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (window.playground_copyable) {
|
||||||
|
Array.from(document.querySelectorAll('pre code')).forEach(function (block) {
|
||||||
|
var pre_block = block.parentNode;
|
||||||
|
if (!pre_block.classList.contains('playground')) {
|
||||||
|
var buttons = pre_block.querySelector(".buttons");
|
||||||
|
if (!buttons) {
|
||||||
|
buttons = document.createElement('div');
|
||||||
|
buttons.className = 'buttons';
|
||||||
|
pre_block.insertBefore(buttons, pre_block.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
var clipButton = document.createElement('button');
|
||||||
|
clipButton.className = 'fa fa-copy clip-button';
|
||||||
|
clipButton.title = 'Copy to clipboard';
|
||||||
|
clipButton.setAttribute('aria-label', clipButton.title);
|
||||||
|
clipButton.innerHTML = '<i class=\"tooltiptext\"></i>';
|
||||||
|
|
||||||
|
buttons.insertBefore(clipButton, buttons.firstChild);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process playground code blocks
|
||||||
|
Array.from(document.querySelectorAll(".playground")).forEach(function (pre_block) {
|
||||||
|
// Add play button
|
||||||
|
var buttons = pre_block.querySelector(".buttons");
|
||||||
|
if (!buttons) {
|
||||||
|
buttons = document.createElement('div');
|
||||||
|
buttons.className = 'buttons';
|
||||||
|
pre_block.insertBefore(buttons, pre_block.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
var runCodeButton = document.createElement('button');
|
||||||
|
runCodeButton.className = 'fa fa-play play-button';
|
||||||
|
runCodeButton.hidden = true;
|
||||||
|
runCodeButton.title = 'Run this code';
|
||||||
|
runCodeButton.setAttribute('aria-label', runCodeButton.title);
|
||||||
|
|
||||||
|
buttons.insertBefore(runCodeButton, buttons.firstChild);
|
||||||
|
runCodeButton.addEventListener('click', function (e) {
|
||||||
|
run_rust_code(pre_block);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (window.playground_copyable) {
|
||||||
|
var copyCodeClipboardButton = document.createElement('button');
|
||||||
|
copyCodeClipboardButton.className = 'fa fa-copy clip-button';
|
||||||
|
copyCodeClipboardButton.innerHTML = '<i class="tooltiptext"></i>';
|
||||||
|
copyCodeClipboardButton.title = 'Copy to clipboard';
|
||||||
|
copyCodeClipboardButton.setAttribute('aria-label', copyCodeClipboardButton.title);
|
||||||
|
|
||||||
|
buttons.insertBefore(copyCodeClipboardButton, buttons.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
let code_block = pre_block.querySelector("code");
|
||||||
|
if (window.ace && code_block.classList.contains("editable")) {
|
||||||
|
var undoChangesButton = document.createElement('button');
|
||||||
|
undoChangesButton.className = 'fa fa-history reset-button';
|
||||||
|
undoChangesButton.title = 'Undo changes';
|
||||||
|
undoChangesButton.setAttribute('aria-label', undoChangesButton.title);
|
||||||
|
|
||||||
|
buttons.insertBefore(undoChangesButton, buttons.firstChild);
|
||||||
|
|
||||||
|
undoChangesButton.addEventListener('click', function () {
|
||||||
|
let editor = window.ace.edit(code_block);
|
||||||
|
editor.setValue(editor.originalCode);
|
||||||
|
editor.clearSelection();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function themes() {
|
||||||
|
var html = document.querySelector('html');
|
||||||
|
var themeToggleButton = document.getElementById('theme-toggle');
|
||||||
|
var themePopup = document.getElementById('theme-list');
|
||||||
|
var themeColorMetaTag = document.querySelector('meta[name="theme-color"]');
|
||||||
|
var stylesheets = {
|
||||||
|
ayuHighlight: document.querySelector("[href$='ayu-highlight.css']"),
|
||||||
|
tomorrowNight: document.querySelector("[href$='tomorrow-night.css']"),
|
||||||
|
highlight: document.querySelector("[href$='highlight.css']"),
|
||||||
|
};
|
||||||
|
|
||||||
|
function showThemes() {
|
||||||
|
themePopup.style.display = 'block';
|
||||||
|
themeToggleButton.setAttribute('aria-expanded', true);
|
||||||
|
themePopup.querySelector("button#" + get_theme()).focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideThemes() {
|
||||||
|
themePopup.style.display = 'none';
|
||||||
|
themeToggleButton.setAttribute('aria-expanded', false);
|
||||||
|
themeToggleButton.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_theme() {
|
||||||
|
var theme;
|
||||||
|
try { theme = localStorage.getItem('mdbook-theme'); } catch (e) { }
|
||||||
|
if (theme === null || theme === undefined) {
|
||||||
|
return default_theme;
|
||||||
|
} else {
|
||||||
|
return theme;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_theme(theme, store = true) {
|
||||||
|
let ace_theme;
|
||||||
|
|
||||||
|
if (theme == 'coal' || theme == 'navy') {
|
||||||
|
stylesheets.ayuHighlight.disabled = true;
|
||||||
|
stylesheets.tomorrowNight.disabled = false;
|
||||||
|
stylesheets.highlight.disabled = true;
|
||||||
|
|
||||||
|
ace_theme = "ace/theme/tomorrow_night";
|
||||||
|
} else if (theme == 'ayu') {
|
||||||
|
stylesheets.ayuHighlight.disabled = false;
|
||||||
|
stylesheets.tomorrowNight.disabled = true;
|
||||||
|
stylesheets.highlight.disabled = true;
|
||||||
|
ace_theme = "ace/theme/tomorrow_night";
|
||||||
|
} else {
|
||||||
|
stylesheets.ayuHighlight.disabled = true;
|
||||||
|
stylesheets.tomorrowNight.disabled = true;
|
||||||
|
stylesheets.highlight.disabled = false;
|
||||||
|
ace_theme = "ace/theme/dawn";
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(function () {
|
||||||
|
themeColorMetaTag.content = getComputedStyle(document.body).backgroundColor;
|
||||||
|
}, 1);
|
||||||
|
|
||||||
|
if (window.ace && window.editors) {
|
||||||
|
window.editors.forEach(function (editor) {
|
||||||
|
editor.setTheme(ace_theme);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var previousTheme = get_theme();
|
||||||
|
|
||||||
|
if (store) {
|
||||||
|
try { localStorage.setItem('mdbook-theme', theme); } catch (e) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
html.classList.remove(previousTheme);
|
||||||
|
html.classList.add(theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set theme
|
||||||
|
var theme = get_theme();
|
||||||
|
|
||||||
|
set_theme(theme, false);
|
||||||
|
|
||||||
|
themeToggleButton.addEventListener('click', function () {
|
||||||
|
if (themePopup.style.display === 'block') {
|
||||||
|
hideThemes();
|
||||||
|
} else {
|
||||||
|
showThemes();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
themePopup.addEventListener('click', function (e) {
|
||||||
|
var theme = e.target.id || e.target.parentElement.id;
|
||||||
|
set_theme(theme);
|
||||||
|
});
|
||||||
|
|
||||||
|
themePopup.addEventListener('focusout', function(e) {
|
||||||
|
// e.relatedTarget is null in Safari and Firefox on macOS (see workaround below)
|
||||||
|
if (!!e.relatedTarget && !themeToggleButton.contains(e.relatedTarget) && !themePopup.contains(e.relatedTarget)) {
|
||||||
|
hideThemes();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Should not be needed, but it works around an issue on macOS & iOS: https://github.com/rust-lang/mdBook/issues/628
|
||||||
|
document.addEventListener('click', function(e) {
|
||||||
|
if (themePopup.style.display === 'block' && !themeToggleButton.contains(e.target) && !themePopup.contains(e.target)) {
|
||||||
|
hideThemes();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('keydown', function (e) {
|
||||||
|
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
|
||||||
|
if (!themePopup.contains(e.target)) { return; }
|
||||||
|
|
||||||
|
switch (e.key) {
|
||||||
|
case 'Escape':
|
||||||
|
e.preventDefault();
|
||||||
|
hideThemes();
|
||||||
|
break;
|
||||||
|
case 'ArrowUp':
|
||||||
|
e.preventDefault();
|
||||||
|
var li = document.activeElement.parentElement;
|
||||||
|
if (li && li.previousElementSibling) {
|
||||||
|
li.previousElementSibling.querySelector('button').focus();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'ArrowDown':
|
||||||
|
e.preventDefault();
|
||||||
|
var li = document.activeElement.parentElement;
|
||||||
|
if (li && li.nextElementSibling) {
|
||||||
|
li.nextElementSibling.querySelector('button').focus();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'Home':
|
||||||
|
e.preventDefault();
|
||||||
|
themePopup.querySelector('li:first-child button').focus();
|
||||||
|
break;
|
||||||
|
case 'End':
|
||||||
|
e.preventDefault();
|
||||||
|
themePopup.querySelector('li:last-child button').focus();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function sidebar() {
|
||||||
|
var html = document.querySelector("html");
|
||||||
|
var sidebar = document.getElementById("sidebar");
|
||||||
|
var sidebarLinks = document.querySelectorAll('#sidebar a');
|
||||||
|
var sidebarToggleButton = document.getElementById("sidebar-toggle");
|
||||||
|
var sidebarResizeHandle = document.getElementById("sidebar-resize-handle");
|
||||||
|
var firstContact = null;
|
||||||
|
|
||||||
|
function showSidebar() {
|
||||||
|
html.classList.remove('sidebar-hidden')
|
||||||
|
html.classList.add('sidebar-visible');
|
||||||
|
Array.from(sidebarLinks).forEach(function (link) {
|
||||||
|
link.setAttribute('tabIndex', 0);
|
||||||
|
});
|
||||||
|
sidebarToggleButton.setAttribute('aria-expanded', true);
|
||||||
|
sidebar.setAttribute('aria-hidden', false);
|
||||||
|
try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var sidebarAnchorToggles = document.querySelectorAll('#sidebar a.toggle');
|
||||||
|
|
||||||
|
function toggleSection(ev) {
|
||||||
|
ev.currentTarget.parentElement.classList.toggle('expanded');
|
||||||
|
}
|
||||||
|
|
||||||
|
Array.from(sidebarAnchorToggles).forEach(function (el) {
|
||||||
|
el.addEventListener('click', toggleSection);
|
||||||
|
});
|
||||||
|
|
||||||
|
function hideSidebar() {
|
||||||
|
html.classList.remove('sidebar-visible')
|
||||||
|
html.classList.add('sidebar-hidden');
|
||||||
|
Array.from(sidebarLinks).forEach(function (link) {
|
||||||
|
link.setAttribute('tabIndex', -1);
|
||||||
|
});
|
||||||
|
sidebarToggleButton.setAttribute('aria-expanded', false);
|
||||||
|
sidebar.setAttribute('aria-hidden', true);
|
||||||
|
try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle sidebar
|
||||||
|
sidebarToggleButton.addEventListener('click', function sidebarToggle() {
|
||||||
|
if (html.classList.contains("sidebar-hidden")) {
|
||||||
|
var current_width = parseInt(
|
||||||
|
document.documentElement.style.getPropertyValue('--sidebar-width'), 10);
|
||||||
|
if (current_width < 150) {
|
||||||
|
document.documentElement.style.setProperty('--sidebar-width', '150px');
|
||||||
|
}
|
||||||
|
showSidebar();
|
||||||
|
} else if (html.classList.contains("sidebar-visible")) {
|
||||||
|
hideSidebar();
|
||||||
|
} else {
|
||||||
|
if (getComputedStyle(sidebar)['transform'] === 'none') {
|
||||||
|
hideSidebar();
|
||||||
|
} else {
|
||||||
|
showSidebar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
sidebarResizeHandle.addEventListener('mousedown', initResize, false);
|
||||||
|
|
||||||
|
function initResize(e) {
|
||||||
|
window.addEventListener('mousemove', resize, false);
|
||||||
|
window.addEventListener('mouseup', stopResize, false);
|
||||||
|
html.classList.add('sidebar-resizing');
|
||||||
|
}
|
||||||
|
function resize(e) {
|
||||||
|
var pos = (e.clientX - sidebar.offsetLeft);
|
||||||
|
if (pos < 20) {
|
||||||
|
hideSidebar();
|
||||||
|
} else {
|
||||||
|
if (html.classList.contains("sidebar-hidden")) {
|
||||||
|
showSidebar();
|
||||||
|
}
|
||||||
|
pos = Math.min(pos, window.innerWidth - 100);
|
||||||
|
document.documentElement.style.setProperty('--sidebar-width', pos + 'px');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//on mouseup remove windows functions mousemove & mouseup
|
||||||
|
function stopResize(e) {
|
||||||
|
html.classList.remove('sidebar-resizing');
|
||||||
|
window.removeEventListener('mousemove', resize, false);
|
||||||
|
window.removeEventListener('mouseup', stopResize, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('touchstart', function (e) {
|
||||||
|
firstContact = {
|
||||||
|
x: e.touches[0].clientX,
|
||||||
|
time: Date.now()
|
||||||
|
};
|
||||||
|
}, { passive: true });
|
||||||
|
|
||||||
|
document.addEventListener('touchmove', function (e) {
|
||||||
|
if (!firstContact)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var curX = e.touches[0].clientX;
|
||||||
|
var xDiff = curX - firstContact.x,
|
||||||
|
tDiff = Date.now() - firstContact.time;
|
||||||
|
|
||||||
|
if (tDiff < 250 && Math.abs(xDiff) >= 150) {
|
||||||
|
if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300))
|
||||||
|
showSidebar();
|
||||||
|
else if (xDiff < 0 && curX < 300)
|
||||||
|
hideSidebar();
|
||||||
|
|
||||||
|
firstContact = null;
|
||||||
|
}
|
||||||
|
}, { passive: true });
|
||||||
|
|
||||||
|
// Scroll sidebar to current active section
|
||||||
|
var activeSection = document.getElementById("sidebar").querySelector(".active");
|
||||||
|
if (activeSection) {
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView
|
||||||
|
activeSection.scrollIntoView({ block: 'center' });
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function chapterNavigation() {
|
||||||
|
document.addEventListener('keydown', function (e) {
|
||||||
|
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
|
||||||
|
if (window.search && window.search.hasFocus()) { return; }
|
||||||
|
|
||||||
|
switch (e.key) {
|
||||||
|
case 'ArrowRight':
|
||||||
|
e.preventDefault();
|
||||||
|
var nextButton = document.querySelector('.nav-chapters.next');
|
||||||
|
if (nextButton) {
|
||||||
|
window.location.href = nextButton.href;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'ArrowLeft':
|
||||||
|
e.preventDefault();
|
||||||
|
var previousButton = document.querySelector('.nav-chapters.previous');
|
||||||
|
if (previousButton) {
|
||||||
|
window.location.href = previousButton.href;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function clipboard() {
|
||||||
|
var clipButtons = document.querySelectorAll('.clip-button');
|
||||||
|
|
||||||
|
function hideTooltip(elem) {
|
||||||
|
elem.firstChild.innerText = "";
|
||||||
|
elem.className = 'fa fa-copy clip-button';
|
||||||
|
}
|
||||||
|
|
||||||
|
function showTooltip(elem, msg) {
|
||||||
|
elem.firstChild.innerText = msg;
|
||||||
|
elem.className = 'fa fa-copy tooltipped';
|
||||||
|
}
|
||||||
|
|
||||||
|
var clipboardSnippets = new ClipboardJS('.clip-button', {
|
||||||
|
text: function (trigger) {
|
||||||
|
hideTooltip(trigger);
|
||||||
|
let playground = trigger.closest("pre");
|
||||||
|
return playground_text(playground);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Array.from(clipButtons).forEach(function (clipButton) {
|
||||||
|
clipButton.addEventListener('mouseout', function (e) {
|
||||||
|
hideTooltip(e.currentTarget);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
clipboardSnippets.on('success', function (e) {
|
||||||
|
e.clearSelection();
|
||||||
|
showTooltip(e.trigger, "Copied!");
|
||||||
|
});
|
||||||
|
|
||||||
|
clipboardSnippets.on('error', function (e) {
|
||||||
|
showTooltip(e.trigger, "Clipboard error!");
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function scrollToTop () {
|
||||||
|
var menuTitle = document.querySelector('.menu-title');
|
||||||
|
|
||||||
|
menuTitle.addEventListener('click', function () {
|
||||||
|
document.scrollingElement.scrollTo({ top: 0, behavior: 'smooth' });
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function controllMenu() {
|
||||||
|
var menu = document.getElementById('menu-bar');
|
||||||
|
|
||||||
|
(function controllPosition() {
|
||||||
|
var scrollTop = document.scrollingElement.scrollTop;
|
||||||
|
var prevScrollTop = scrollTop;
|
||||||
|
var minMenuY = -menu.clientHeight - 50;
|
||||||
|
// When the script loads, the page can be at any scroll (e.g. if you reforesh it).
|
||||||
|
menu.style.top = scrollTop + 'px';
|
||||||
|
// Same as parseInt(menu.style.top.slice(0, -2), but faster
|
||||||
|
var topCache = menu.style.top.slice(0, -2);
|
||||||
|
menu.classList.remove('sticky');
|
||||||
|
var stickyCache = false; // Same as menu.classList.contains('sticky'), but faster
|
||||||
|
document.addEventListener('scroll', function () {
|
||||||
|
scrollTop = Math.max(document.scrollingElement.scrollTop, 0);
|
||||||
|
// `null` means that it doesn't need to be updated
|
||||||
|
var nextSticky = null;
|
||||||
|
var nextTop = null;
|
||||||
|
var scrollDown = scrollTop > prevScrollTop;
|
||||||
|
var menuPosAbsoluteY = topCache - scrollTop;
|
||||||
|
if (scrollDown) {
|
||||||
|
nextSticky = false;
|
||||||
|
if (menuPosAbsoluteY > 0) {
|
||||||
|
nextTop = prevScrollTop;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (menuPosAbsoluteY > 0) {
|
||||||
|
nextSticky = true;
|
||||||
|
} else if (menuPosAbsoluteY < minMenuY) {
|
||||||
|
nextTop = prevScrollTop + minMenuY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nextSticky === true && stickyCache === false) {
|
||||||
|
menu.classList.add('sticky');
|
||||||
|
stickyCache = true;
|
||||||
|
} else if (nextSticky === false && stickyCache === true) {
|
||||||
|
menu.classList.remove('sticky');
|
||||||
|
stickyCache = false;
|
||||||
|
}
|
||||||
|
if (nextTop !== null) {
|
||||||
|
menu.style.top = nextTop + 'px';
|
||||||
|
topCache = nextTop;
|
||||||
|
}
|
||||||
|
prevScrollTop = scrollTop;
|
||||||
|
}, { passive: true });
|
||||||
|
})();
|
||||||
|
(function controllBorder() {
|
||||||
|
menu.classList.remove('bordered');
|
||||||
|
document.addEventListener('scroll', function () {
|
||||||
|
if (menu.offsetTop === 0) {
|
||||||
|
menu.classList.remove('bordered');
|
||||||
|
} else {
|
||||||
|
menu.classList.add('bordered');
|
||||||
|
}
|
||||||
|
}, { passive: true });
|
||||||
|
})();
|
||||||
|
})();
|
||||||
File diff suppressed because one or more lines are too long
@ -1,3 +0,0 @@
|
|||||||
target
|
|
||||||
Cargo.lock
|
|
||||||
.idea
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
[workspace]
|
|
||||||
members = [
|
|
||||||
"ch01-01",
|
|
||||||
"ch01-02",
|
|
||||||
"ch01-03",
|
|
||||||
"ch02-02",
|
|
||||||
# "ch02-03",
|
|
||||||
# "ch03-02",
|
|
||||||
]
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "ch01-01"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Runji Wang <wangrunji0408@163.com>"]
|
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
# ANCHOR: spin
|
|
||||||
spin = "0.7"
|
|
||||||
# ANCHOR_END: spin
|
|
||||||
# ANCHOR: downcast
|
|
||||||
downcast-rs = { version = "1.2.0", default-features = false }
|
|
||||||
# ANCHOR_END: downcast
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
|
|
||||||
extern crate alloc;
|
|
||||||
|
|
||||||
mod object;
|
|
||||||
@ -1,84 +0,0 @@
|
|||||||
#![allow(dead_code)]
|
|
||||||
use super::{KernelObject, KoID, String};
|
|
||||||
|
|
||||||
// ANCHOR: dummy_def
|
|
||||||
use spin::Mutex;
|
|
||||||
|
|
||||||
/// 空对象
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct DummyObject {
|
|
||||||
id: KoID,
|
|
||||||
inner: Mutex<DummyObjectInner>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `DummyObject` 的内部可变部分
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
struct DummyObjectInner {
|
|
||||||
name: String,
|
|
||||||
}
|
|
||||||
// ANCHOR_END: dummy_def
|
|
||||||
|
|
||||||
// ANCHOR: dummy_new
|
|
||||||
use alloc::sync::Arc;
|
|
||||||
use core::sync::atomic::*;
|
|
||||||
|
|
||||||
impl DummyObject {
|
|
||||||
/// 创建一个新 `DummyObject`
|
|
||||||
pub fn new() -> Arc<Self> {
|
|
||||||
Arc::new(DummyObject {
|
|
||||||
id: Self::new_koid(),
|
|
||||||
inner: Default::default(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 生成一个唯一的 ID
|
|
||||||
fn new_koid() -> KoID {
|
|
||||||
static NEXT_KOID: AtomicU64 = AtomicU64::new(1024);
|
|
||||||
NEXT_KOID.fetch_add(1, Ordering::SeqCst)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ANCHOR_END: dummy_new
|
|
||||||
|
|
||||||
// ANCHOR: dummy_impl
|
|
||||||
impl KernelObject for DummyObject {
|
|
||||||
fn id(&self) -> KoID {
|
|
||||||
self.id
|
|
||||||
}
|
|
||||||
fn type_name(&self) -> &str {
|
|
||||||
"DummyObject"
|
|
||||||
}
|
|
||||||
fn name(&self) -> String {
|
|
||||||
self.inner.lock().name.clone()
|
|
||||||
}
|
|
||||||
fn set_name(&self, name: &str) {
|
|
||||||
self.inner.lock().name = String::from(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ANCHOR_END: dummy_impl
|
|
||||||
|
|
||||||
// ANCHOR: dummy_test
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
#[test]
|
|
||||||
fn dummy_object() {
|
|
||||||
let o1 = DummyObject::new();
|
|
||||||
let o2 = DummyObject::new();
|
|
||||||
assert_ne!(o1.id(), o2.id());
|
|
||||||
assert_eq!(o1.type_name(), "DummyObject");
|
|
||||||
assert_eq!(o1.name(), "");
|
|
||||||
o1.set_name("object1");
|
|
||||||
assert_eq!(o1.name(), "object1");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ANCHOR_END: dummy_test
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
// ANCHOR: downcast_test
|
|
||||||
#[test]
|
|
||||||
fn downcast() {
|
|
||||||
let dummy = DummyObject::new();
|
|
||||||
let object: Arc<dyn KernelObject> = dummy;
|
|
||||||
let _result: Arc<DummyObject> = object.downcast_arc::<DummyObject>().unwrap();
|
|
||||||
}
|
|
||||||
// ANCHOR_END: downcast_test
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "ch01-02"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Runji Wang <wangrunji0408@163.com>"]
|
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
spin = "0.7"
|
|
||||||
downcast-rs = { version = "1.2.0", default-features = false }
|
|
||||||
# ANCHOR: bitflags
|
|
||||||
bitflags = "1.2"
|
|
||||||
# ANCHOR_END: bitflags
|
|
||||||
@ -1,232 +0,0 @@
|
|||||||
// ANCHOR: result
|
|
||||||
///
|
|
||||||
pub type ZxResult<T> = Result<T, ZxError>;
|
|
||||||
// ANCHOR_END: result
|
|
||||||
|
|
||||||
// ANCHOR: error_begin
|
|
||||||
/// Zircon statuses are signed 32 bit integers. The space of values is
|
|
||||||
/// divided as follows:
|
|
||||||
/// - The zero value is for the OK status.
|
|
||||||
/// - Negative values are defined by the system, in this file.
|
|
||||||
/// - Positive values are reserved for protocol-specific error values,
|
|
||||||
/// and will never be defined by the system.
|
|
||||||
#[allow(non_camel_case_types, dead_code)]
|
|
||||||
#[repr(i32)]
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub enum ZxError {
|
|
||||||
OK = 0,
|
|
||||||
|
|
||||||
// ======= Internal failures =======
|
|
||||||
/// The system encountered an otherwise unspecified error
|
|
||||||
/// while performing the operation.
|
|
||||||
INTERNAL = -1,
|
|
||||||
|
|
||||||
/// The operation is not implemented, supported,
|
|
||||||
/// or enabled.
|
|
||||||
NOT_SUPPORTED = -2,
|
|
||||||
// ANCHOR_END: error_begin
|
|
||||||
/// The system was not able to allocate some resource
|
|
||||||
/// needed for the operation.
|
|
||||||
NO_RESOURCES = -3,
|
|
||||||
|
|
||||||
/// The system was not able to allocate memory needed
|
|
||||||
/// for the operation.
|
|
||||||
NO_MEMORY = -4,
|
|
||||||
|
|
||||||
// -5 used to be ZX_ERR_CALL_FAILED.
|
|
||||||
/// The system call was interrupted, but should be
|
|
||||||
/// retried. This should not be seen outside of the VDSO.
|
|
||||||
INTERNAL_INTR_RETRY = -6,
|
|
||||||
|
|
||||||
// ======= Parameter errors =======
|
|
||||||
/// an argument is invalid, ex. null pointer
|
|
||||||
INVALID_ARGS = -10,
|
|
||||||
|
|
||||||
/// A specified handle value does not refer to a handle.
|
|
||||||
BAD_HANDLE = -11,
|
|
||||||
|
|
||||||
/// The subject of the operation is the wrong type to
|
|
||||||
/// perform the operation.
|
|
||||||
/// Example: Attempting a message_read on a thread handle.
|
|
||||||
WRONG_TYPE = -12,
|
|
||||||
|
|
||||||
/// The specified syscall number is invalid.
|
|
||||||
BAD_SYSCALL = -13,
|
|
||||||
|
|
||||||
/// An argument is outside the valid range for this
|
|
||||||
/// operation.
|
|
||||||
OUT_OF_RANGE = -14,
|
|
||||||
|
|
||||||
/// A caller provided buffer is too small for
|
|
||||||
/// this operation.
|
|
||||||
BUFFER_TOO_SMALL = -15,
|
|
||||||
|
|
||||||
// ======= Precondition or state errors =======
|
|
||||||
/// operation failed because the current state of the
|
|
||||||
/// object does not allow it, or a precondition of the operation is
|
|
||||||
/// not satisfied
|
|
||||||
BAD_STATE = -20,
|
|
||||||
|
|
||||||
/// The time limit for the operation elapsed before
|
|
||||||
/// the operation completed.
|
|
||||||
TIMED_OUT = -21,
|
|
||||||
|
|
||||||
/// The operation cannot be performed currently but
|
|
||||||
/// potentially could succeed if the caller waits for a prerequisite
|
|
||||||
/// to be satisfied, for example waiting for a handle to be readable
|
|
||||||
/// or writable.
|
|
||||||
/// Example: Attempting to read from a channel that has no
|
|
||||||
/// messages waiting but has an open remote will return ZX_ERR_SHOULD_WAIT.
|
|
||||||
/// Attempting to read from a channel that has no messages waiting
|
|
||||||
/// and has a closed remote end will return ZX_ERR_PEER_CLOSED.
|
|
||||||
SHOULD_WAIT = -22,
|
|
||||||
|
|
||||||
/// The in-progress operation (e.g. a wait) has been
|
|
||||||
/// canceled.
|
|
||||||
CANCELED = -23,
|
|
||||||
|
|
||||||
/// The operation failed because the remote end of the
|
|
||||||
/// subject of the operation was closed.
|
|
||||||
PEER_CLOSED = -24,
|
|
||||||
|
|
||||||
/// The requested entity is not found.
|
|
||||||
NOT_FOUND = -25,
|
|
||||||
|
|
||||||
/// An object with the specified identifier
|
|
||||||
/// already exists.
|
|
||||||
/// Example: Attempting to create a file when a file already exists
|
|
||||||
/// with that name.
|
|
||||||
ALREADY_EXISTS = -26,
|
|
||||||
|
|
||||||
/// The operation failed because the named entity
|
|
||||||
/// is already owned or controlled by another entity. The operation
|
|
||||||
/// could succeed later if the current owner releases the entity.
|
|
||||||
ALREADY_BOUND = -27,
|
|
||||||
|
|
||||||
/// The subject of the operation is currently unable
|
|
||||||
/// to perform the operation.
|
|
||||||
/// Note: This is used when there's no direct way for the caller to
|
|
||||||
/// observe when the subject will be able to perform the operation
|
|
||||||
/// and should thus retry.
|
|
||||||
UNAVAILABLE = -28,
|
|
||||||
|
|
||||||
// ======= Permission check errors =======
|
|
||||||
/// The caller did not have permission to perform
|
|
||||||
/// the specified operation.
|
|
||||||
ACCESS_DENIED = -30,
|
|
||||||
|
|
||||||
// ======= Input-output errors =======
|
|
||||||
/// Otherwise unspecified error occurred during I/O.
|
|
||||||
IO = -40,
|
|
||||||
|
|
||||||
/// The entity the I/O operation is being performed on
|
|
||||||
/// rejected the operation.
|
|
||||||
/// Example: an I2C device NAK'ing a transaction or a disk controller
|
|
||||||
/// rejecting an invalid command, or a stalled USB endpoint.
|
|
||||||
IO_REFUSED = -41,
|
|
||||||
|
|
||||||
/// The data in the operation failed an integrity
|
|
||||||
/// check and is possibly corrupted.
|
|
||||||
/// Example: CRC or Parity error.
|
|
||||||
IO_DATA_INTEGRITY = -42,
|
|
||||||
|
|
||||||
/// The data in the operation is currently unavailable
|
|
||||||
/// and may be permanently lost.
|
|
||||||
/// Example: A disk block is irrecoverably damaged.
|
|
||||||
IO_DATA_LOSS = -43,
|
|
||||||
|
|
||||||
/// The device is no longer available (has been
|
|
||||||
/// unplugged from the system, powered down, or the driver has been
|
|
||||||
/// unloaded,
|
|
||||||
IO_NOT_PRESENT = -44,
|
|
||||||
|
|
||||||
/// More data was received from the device than expected.
|
|
||||||
/// Example: a USB "babble" error due to a device sending more data than
|
|
||||||
/// the host queued to receive.
|
|
||||||
IO_OVERRUN = -45,
|
|
||||||
|
|
||||||
/// An operation did not complete within the required timeframe.
|
|
||||||
/// Example: A USB isochronous transfer that failed to complete due to an overrun or underrun.
|
|
||||||
IO_MISSED_DEADLINE = -46,
|
|
||||||
|
|
||||||
/// The data in the operation is invalid parameter or is out of range.
|
|
||||||
/// Example: A USB transfer that failed to complete with TRB Error
|
|
||||||
IO_INVALID = -47,
|
|
||||||
|
|
||||||
// ======== Filesystem Errors ========
|
|
||||||
/// Path name is too long.
|
|
||||||
BAD_PATH = -50,
|
|
||||||
|
|
||||||
/// Object is not a directory or does not support
|
|
||||||
/// directory operations.
|
|
||||||
/// Example: Attempted to open a file as a directory or
|
|
||||||
/// attempted to do directory operations on a file.
|
|
||||||
NOT_DIR = -51,
|
|
||||||
|
|
||||||
/// Object is not a regular file.
|
|
||||||
NOT_FILE = -52,
|
|
||||||
|
|
||||||
/// This operation would cause a file to exceed a
|
|
||||||
/// filesystem-specific size limit
|
|
||||||
FILE_BIG = -53,
|
|
||||||
|
|
||||||
/// Filesystem or device space is exhausted.
|
|
||||||
NO_SPACE = -54,
|
|
||||||
|
|
||||||
/// Directory is not empty.
|
|
||||||
NOT_EMPTY = -55,
|
|
||||||
|
|
||||||
// ======== Flow Control ========
|
|
||||||
// These are not errors, as such, and will never be returned
|
|
||||||
// by a syscall or public API. They exist to allow callbacks
|
|
||||||
// to request changes in operation.
|
|
||||||
/// Do not call again.
|
|
||||||
/// Example: A notification callback will be called on every
|
|
||||||
/// event until it returns something other than ZX_OK.
|
|
||||||
/// This status allows differentiation between "stop due to
|
|
||||||
/// an error" and "stop because the work is done."
|
|
||||||
STOP = -60,
|
|
||||||
|
|
||||||
/// Advance to the next item.
|
|
||||||
/// Example: A notification callback will use this response
|
|
||||||
/// to indicate it did not "consume" an item passed to it,
|
|
||||||
/// but by choice, not due to an error condition.
|
|
||||||
NEXT = -61,
|
|
||||||
|
|
||||||
/// Ownership of the item has moved to an asynchronous worker.
|
|
||||||
///
|
|
||||||
/// Unlike ZX_ERR_STOP, which implies that iteration on an object
|
|
||||||
/// should stop, and ZX_ERR_NEXT, which implies that iteration
|
|
||||||
/// should continue to the next item, ZX_ERR_ASYNC implies
|
|
||||||
/// that an asynchronous worker is responsible for continuing iteration.
|
|
||||||
///
|
|
||||||
/// Example: A notification callback will be called on every
|
|
||||||
/// event, but one event needs to handle some work asynchronously
|
|
||||||
/// before it can continue. ZX_ERR_ASYNC implies the worker is
|
|
||||||
/// responsible for resuming iteration once its work has completed.
|
|
||||||
ASYNC = -62,
|
|
||||||
|
|
||||||
// ======== Network-related errors ========
|
|
||||||
/// Specified protocol is not
|
|
||||||
/// supported.
|
|
||||||
PROTOCOL_NOT_SUPPORTED = -70,
|
|
||||||
|
|
||||||
/// Host is unreachable.
|
|
||||||
ADDRESS_UNREACHABLE = -71,
|
|
||||||
|
|
||||||
/// Address is being used by someone else.
|
|
||||||
ADDRESS_IN_USE = -72,
|
|
||||||
|
|
||||||
/// Socket is not connected.
|
|
||||||
NOT_CONNECTED = -73,
|
|
||||||
|
|
||||||
/// Remote peer rejected the connection.
|
|
||||||
CONNECTION_REFUSED = -74,
|
|
||||||
|
|
||||||
/// Connection was reset.
|
|
||||||
CONNECTION_RESET = -75,
|
|
||||||
// ANCHOR: error_end
|
|
||||||
/// Connection was aborted.
|
|
||||||
CONNECTION_ABORTED = -76,
|
|
||||||
}
|
|
||||||
// ANCHOR_END: error_end
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
|
|
||||||
extern crate alloc;
|
|
||||||
|
|
||||||
mod error;
|
|
||||||
mod object;
|
|
||||||
mod task;
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
// ANCHOR: handle
|
|
||||||
use super::{KernelObject, Rights};
|
|
||||||
use alloc::sync::Arc;
|
|
||||||
|
|
||||||
/// 内核对象句柄
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Handle {
|
|
||||||
pub object: Arc<dyn KernelObject>,
|
|
||||||
pub rights: Rights,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Handle {
|
|
||||||
/// 创建一个新句柄
|
|
||||||
pub fn new(object: Arc<dyn KernelObject>, rights: Rights) -> Self {
|
|
||||||
Handle { object, rights }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ANCHOR_END: handle
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::object::DummyObject;
|
|
||||||
#[test]
|
|
||||||
fn new_obj_handle() {
|
|
||||||
let obj = DummyObject::new();
|
|
||||||
let handle1 = Handle::new(obj.clone(), Rights::BASIC);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
use super::*;
|
|
||||||
|
|
||||||
mod process;
|
|
||||||
@ -1,119 +0,0 @@
|
|||||||
use super::*;
|
|
||||||
use crate::error::*;
|
|
||||||
use crate::object::*;
|
|
||||||
use alloc::collections::BTreeMap;
|
|
||||||
use alloc::sync::Arc;
|
|
||||||
use spin::Mutex;
|
|
||||||
|
|
||||||
// ANCHOR: process
|
|
||||||
/// 进程对象
|
|
||||||
pub struct Process {
|
|
||||||
base: KObjectBase,
|
|
||||||
inner: Mutex<ProcessInner>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_kobject!(Process);
|
|
||||||
|
|
||||||
struct ProcessInner {
|
|
||||||
handles: BTreeMap<HandleValue, Handle>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type HandleValue = u32;
|
|
||||||
|
|
||||||
impl Process {
|
|
||||||
/// 创建一个新的进程对象
|
|
||||||
pub fn new() -> Arc<Self> {
|
|
||||||
Arc::new(Process {
|
|
||||||
base: KObjectBase::default(),
|
|
||||||
inner: Mutex::new(ProcessInner {
|
|
||||||
handles: BTreeMap::default(),
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// ANCHOR_END: process
|
|
||||||
|
|
||||||
// ANCHOR: add_remove_handle
|
|
||||||
/// 添加一个新的对象句柄
|
|
||||||
pub fn add_handle(&self, handle: Handle) -> HandleValue {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
let value = (0 as HandleValue..)
|
|
||||||
.find(|idx| !inner.handles.contains_key(idx))
|
|
||||||
.unwrap();
|
|
||||||
inner.handles.insert(value, handle);
|
|
||||||
value
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 删除一个对象句柄
|
|
||||||
pub fn remove_handle(&self, handle_value: HandleValue) {
|
|
||||||
self.inner.lock().handles.remove(&handle_value);
|
|
||||||
}
|
|
||||||
// ANCHOR_END: add_remove_handle
|
|
||||||
|
|
||||||
// ANCHOR: get_object_with_rights
|
|
||||||
/// 根据句柄值查找内核对象,并检查权限
|
|
||||||
pub fn get_object_with_rights<T: KernelObject>(
|
|
||||||
&self,
|
|
||||||
handle_value: HandleValue,
|
|
||||||
desired_rights: Rights,
|
|
||||||
) -> ZxResult<Arc<T>> {
|
|
||||||
let handle = self
|
|
||||||
.inner
|
|
||||||
.lock()
|
|
||||||
.handles
|
|
||||||
.get(&handle_value)
|
|
||||||
.ok_or(ZxError::BAD_HANDLE)?
|
|
||||||
.clone();
|
|
||||||
// check type before rights
|
|
||||||
let object = handle
|
|
||||||
.object
|
|
||||||
.downcast_arc::<T>()
|
|
||||||
.map_err(|_| ZxError::WRONG_TYPE)?;
|
|
||||||
if !handle.rights.contains(desired_rights) {
|
|
||||||
return Err(ZxError::ACCESS_DENIED);
|
|
||||||
}
|
|
||||||
Ok(object)
|
|
||||||
}
|
|
||||||
// ANCHOR_END: get_object_with_rights
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use alloc::format;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn new_proc() {
|
|
||||||
let proc = Process::new();
|
|
||||||
assert_eq!(proc.type_name(), "Process");
|
|
||||||
assert_eq!(proc.name(), "");
|
|
||||||
proc.set_name("proc1");
|
|
||||||
assert_eq!(proc.name(), "proc1");
|
|
||||||
assert_eq!(
|
|
||||||
format!("{:?}", proc),
|
|
||||||
format!("Process({}, \"proc1\")", proc.id())
|
|
||||||
);
|
|
||||||
|
|
||||||
let obj: Arc<dyn KernelObject> = proc;
|
|
||||||
assert_eq!(obj.type_name(), "Process");
|
|
||||||
assert_eq!(obj.name(), "proc1");
|
|
||||||
obj.set_name("proc2");
|
|
||||||
assert_eq!(obj.name(), "proc2");
|
|
||||||
assert_eq!(
|
|
||||||
format!("{:?}", obj),
|
|
||||||
format!("Process({}, \"proc2\")", obj.id())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn proc_handle() {
|
|
||||||
let proc = Process::new();
|
|
||||||
let handle = Handle::new(proc.clone(), Rights::DEFAULT_PROCESS);
|
|
||||||
let handle_value = proc.add_handle(handle);
|
|
||||||
|
|
||||||
let object1: Arc<Process> = proc
|
|
||||||
.get_object_with_rights(handle_value, Rights::DEFAULT_PROCESS)
|
|
||||||
.expect("failed to get object");
|
|
||||||
assert!(Arc::ptr_eq(&object1, &proc));
|
|
||||||
|
|
||||||
proc.remove_handle(handle_value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "ch01-03"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Runji Wang <wangrunji0408@163.com>"]
|
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
spin = "0.7"
|
|
||||||
downcast-rs = { version = "1.2.0", default-features = false }
|
|
||||||
bitflags = "1.2"
|
|
||||||
@ -1,232 +0,0 @@
|
|||||||
// ANCHOR: result
|
|
||||||
///
|
|
||||||
pub type ZxResult<T = ()> = Result<T, ZxError>;
|
|
||||||
// ANCHOR_END: result
|
|
||||||
|
|
||||||
// ANCHOR: error_begin
|
|
||||||
/// Zircon statuses are signed 32 bit integers. The space of values is
|
|
||||||
/// divided as follows:
|
|
||||||
/// - The zero value is for the OK status.
|
|
||||||
/// - Negative values are defined by the system, in this file.
|
|
||||||
/// - Positive values are reserved for protocol-specific error values,
|
|
||||||
/// and will never be defined by the system.
|
|
||||||
#[allow(non_camel_case_types, dead_code)]
|
|
||||||
#[repr(i32)]
|
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
|
||||||
pub enum ZxError {
|
|
||||||
OK = 0,
|
|
||||||
|
|
||||||
// ======= Internal failures =======
|
|
||||||
/// The system encountered an otherwise unspecified error
|
|
||||||
/// while performing the operation.
|
|
||||||
INTERNAL = -1,
|
|
||||||
|
|
||||||
/// The operation is not implemented, supported,
|
|
||||||
/// or enabled.
|
|
||||||
NOT_SUPPORTED = -2,
|
|
||||||
// ANCHOR_END: error_begin
|
|
||||||
/// The system was not able to allocate some resource
|
|
||||||
/// needed for the operation.
|
|
||||||
NO_RESOURCES = -3,
|
|
||||||
|
|
||||||
/// The system was not able to allocate memory needed
|
|
||||||
/// for the operation.
|
|
||||||
NO_MEMORY = -4,
|
|
||||||
|
|
||||||
// -5 used to be ZX_ERR_CALL_FAILED.
|
|
||||||
/// The system call was interrupted, but should be
|
|
||||||
/// retried. This should not be seen outside of the VDSO.
|
|
||||||
INTERNAL_INTR_RETRY = -6,
|
|
||||||
|
|
||||||
// ======= Parameter errors =======
|
|
||||||
/// an argument is invalid, ex. null pointer
|
|
||||||
INVALID_ARGS = -10,
|
|
||||||
|
|
||||||
/// A specified handle value does not refer to a handle.
|
|
||||||
BAD_HANDLE = -11,
|
|
||||||
|
|
||||||
/// The subject of the operation is the wrong type to
|
|
||||||
/// perform the operation.
|
|
||||||
/// Example: Attempting a message_read on a thread handle.
|
|
||||||
WRONG_TYPE = -12,
|
|
||||||
|
|
||||||
/// The specified syscall number is invalid.
|
|
||||||
BAD_SYSCALL = -13,
|
|
||||||
|
|
||||||
/// An argument is outside the valid range for this
|
|
||||||
/// operation.
|
|
||||||
OUT_OF_RANGE = -14,
|
|
||||||
|
|
||||||
/// A caller provided buffer is too small for
|
|
||||||
/// this operation.
|
|
||||||
BUFFER_TOO_SMALL = -15,
|
|
||||||
|
|
||||||
// ======= Precondition or state errors =======
|
|
||||||
/// operation failed because the current state of the
|
|
||||||
/// object does not allow it, or a precondition of the operation is
|
|
||||||
/// not satisfied
|
|
||||||
BAD_STATE = -20,
|
|
||||||
|
|
||||||
/// The time limit for the operation elapsed before
|
|
||||||
/// the operation completed.
|
|
||||||
TIMED_OUT = -21,
|
|
||||||
|
|
||||||
/// The operation cannot be performed currently but
|
|
||||||
/// potentially could succeed if the caller waits for a prerequisite
|
|
||||||
/// to be satisfied, for example waiting for a handle to be readable
|
|
||||||
/// or writable.
|
|
||||||
/// Example: Attempting to read from a channel that has no
|
|
||||||
/// messages waiting but has an open remote will return ZX_ERR_SHOULD_WAIT.
|
|
||||||
/// Attempting to read from a channel that has no messages waiting
|
|
||||||
/// and has a closed remote end will return ZX_ERR_PEER_CLOSED.
|
|
||||||
SHOULD_WAIT = -22,
|
|
||||||
|
|
||||||
/// The in-progress operation (e.g. a wait) has been
|
|
||||||
/// canceled.
|
|
||||||
CANCELED = -23,
|
|
||||||
|
|
||||||
/// The operation failed because the remote end of the
|
|
||||||
/// subject of the operation was closed.
|
|
||||||
PEER_CLOSED = -24,
|
|
||||||
|
|
||||||
/// The requested entity is not found.
|
|
||||||
NOT_FOUND = -25,
|
|
||||||
|
|
||||||
/// An object with the specified identifier
|
|
||||||
/// already exists.
|
|
||||||
/// Example: Attempting to create a file when a file already exists
|
|
||||||
/// with that name.
|
|
||||||
ALREADY_EXISTS = -26,
|
|
||||||
|
|
||||||
/// The operation failed because the named entity
|
|
||||||
/// is already owned or controlled by another entity. The operation
|
|
||||||
/// could succeed later if the current owner releases the entity.
|
|
||||||
ALREADY_BOUND = -27,
|
|
||||||
|
|
||||||
/// The subject of the operation is currently unable
|
|
||||||
/// to perform the operation.
|
|
||||||
/// Note: This is used when there's no direct way for the caller to
|
|
||||||
/// observe when the subject will be able to perform the operation
|
|
||||||
/// and should thus retry.
|
|
||||||
UNAVAILABLE = -28,
|
|
||||||
|
|
||||||
// ======= Permission check errors =======
|
|
||||||
/// The caller did not have permission to perform
|
|
||||||
/// the specified operation.
|
|
||||||
ACCESS_DENIED = -30,
|
|
||||||
|
|
||||||
// ======= Input-output errors =======
|
|
||||||
/// Otherwise unspecified error occurred during I/O.
|
|
||||||
IO = -40,
|
|
||||||
|
|
||||||
/// The entity the I/O operation is being performed on
|
|
||||||
/// rejected the operation.
|
|
||||||
/// Example: an I2C device NAK'ing a transaction or a disk controller
|
|
||||||
/// rejecting an invalid command, or a stalled USB endpoint.
|
|
||||||
IO_REFUSED = -41,
|
|
||||||
|
|
||||||
/// The data in the operation failed an integrity
|
|
||||||
/// check and is possibly corrupted.
|
|
||||||
/// Example: CRC or Parity error.
|
|
||||||
IO_DATA_INTEGRITY = -42,
|
|
||||||
|
|
||||||
/// The data in the operation is currently unavailable
|
|
||||||
/// and may be permanently lost.
|
|
||||||
/// Example: A disk block is irrecoverably damaged.
|
|
||||||
IO_DATA_LOSS = -43,
|
|
||||||
|
|
||||||
/// The device is no longer available (has been
|
|
||||||
/// unplugged from the system, powered down, or the driver has been
|
|
||||||
/// unloaded,
|
|
||||||
IO_NOT_PRESENT = -44,
|
|
||||||
|
|
||||||
/// More data was received from the device than expected.
|
|
||||||
/// Example: a USB "babble" error due to a device sending more data than
|
|
||||||
/// the host queued to receive.
|
|
||||||
IO_OVERRUN = -45,
|
|
||||||
|
|
||||||
/// An operation did not complete within the required timeframe.
|
|
||||||
/// Example: A USB isochronous transfer that failed to complete due to an overrun or underrun.
|
|
||||||
IO_MISSED_DEADLINE = -46,
|
|
||||||
|
|
||||||
/// The data in the operation is invalid parameter or is out of range.
|
|
||||||
/// Example: A USB transfer that failed to complete with TRB Error
|
|
||||||
IO_INVALID = -47,
|
|
||||||
|
|
||||||
// ======== Filesystem Errors ========
|
|
||||||
/// Path name is too long.
|
|
||||||
BAD_PATH = -50,
|
|
||||||
|
|
||||||
/// Object is not a directory or does not support
|
|
||||||
/// directory operations.
|
|
||||||
/// Example: Attempted to open a file as a directory or
|
|
||||||
/// attempted to do directory operations on a file.
|
|
||||||
NOT_DIR = -51,
|
|
||||||
|
|
||||||
/// Object is not a regular file.
|
|
||||||
NOT_FILE = -52,
|
|
||||||
|
|
||||||
/// This operation would cause a file to exceed a
|
|
||||||
/// filesystem-specific size limit
|
|
||||||
FILE_BIG = -53,
|
|
||||||
|
|
||||||
/// Filesystem or device space is exhausted.
|
|
||||||
NO_SPACE = -54,
|
|
||||||
|
|
||||||
/// Directory is not empty.
|
|
||||||
NOT_EMPTY = -55,
|
|
||||||
|
|
||||||
// ======== Flow Control ========
|
|
||||||
// These are not errors, as such, and will never be returned
|
|
||||||
// by a syscall or public API. They exist to allow callbacks
|
|
||||||
// to request changes in operation.
|
|
||||||
/// Do not call again.
|
|
||||||
/// Example: A notification callback will be called on every
|
|
||||||
/// event until it returns something other than ZX_OK.
|
|
||||||
/// This status allows differentiation between "stop due to
|
|
||||||
/// an error" and "stop because the work is done."
|
|
||||||
STOP = -60,
|
|
||||||
|
|
||||||
/// Advance to the next item.
|
|
||||||
/// Example: A notification callback will use this response
|
|
||||||
/// to indicate it did not "consume" an item passed to it,
|
|
||||||
/// but by choice, not due to an error condition.
|
|
||||||
NEXT = -61,
|
|
||||||
|
|
||||||
/// Ownership of the item has moved to an asynchronous worker.
|
|
||||||
///
|
|
||||||
/// Unlike ZX_ERR_STOP, which implies that iteration on an object
|
|
||||||
/// should stop, and ZX_ERR_NEXT, which implies that iteration
|
|
||||||
/// should continue to the next item, ZX_ERR_ASYNC implies
|
|
||||||
/// that an asynchronous worker is responsible for continuing iteration.
|
|
||||||
///
|
|
||||||
/// Example: A notification callback will be called on every
|
|
||||||
/// event, but one event needs to handle some work asynchronously
|
|
||||||
/// before it can continue. ZX_ERR_ASYNC implies the worker is
|
|
||||||
/// responsible for resuming iteration once its work has completed.
|
|
||||||
ASYNC = -62,
|
|
||||||
|
|
||||||
// ======== Network-related errors ========
|
|
||||||
/// Specified protocol is not
|
|
||||||
/// supported.
|
|
||||||
PROTOCOL_NOT_SUPPORTED = -70,
|
|
||||||
|
|
||||||
/// Host is unreachable.
|
|
||||||
ADDRESS_UNREACHABLE = -71,
|
|
||||||
|
|
||||||
/// Address is being used by someone else.
|
|
||||||
ADDRESS_IN_USE = -72,
|
|
||||||
|
|
||||||
/// Socket is not connected.
|
|
||||||
NOT_CONNECTED = -73,
|
|
||||||
|
|
||||||
/// Remote peer rejected the connection.
|
|
||||||
CONNECTION_REFUSED = -74,
|
|
||||||
|
|
||||||
/// Connection was reset.
|
|
||||||
CONNECTION_RESET = -75,
|
|
||||||
// ANCHOR: error_end
|
|
||||||
/// Connection was aborted.
|
|
||||||
CONNECTION_ABORTED = -76,
|
|
||||||
}
|
|
||||||
// ANCHOR_END: error_end
|
|
||||||
@ -1,184 +0,0 @@
|
|||||||
use {
|
|
||||||
super::*,
|
|
||||||
crate::error::*,
|
|
||||||
crate::object::*,
|
|
||||||
alloc::collections::VecDeque,
|
|
||||||
alloc::sync::{Arc, Weak},
|
|
||||||
alloc::vec::Vec,
|
|
||||||
core::sync::atomic::{AtomicU32, Ordering},
|
|
||||||
spin::Mutex,
|
|
||||||
};
|
|
||||||
|
|
||||||
// ANCHOR: Channel
|
|
||||||
pub struct Channel {
|
|
||||||
base: KObjectBase,
|
|
||||||
peer: Weak<Channel>,
|
|
||||||
recv_queue: Mutex<VecDeque<T>>,
|
|
||||||
next_txid: AtomicU32,
|
|
||||||
}
|
|
||||||
|
|
||||||
type T = MessagePacket;
|
|
||||||
type TxID = u32;
|
|
||||||
|
|
||||||
impl_kobject!(Channel
|
|
||||||
fn peer(&self) -> ZxResult<Arc<dyn KernelObject>> {
|
|
||||||
let peer = self.peer.upgrade().ok_or(ZxError::PEER_CLOSED)?;
|
|
||||||
Ok(peer)
|
|
||||||
}
|
|
||||||
fn related_koid(&self) -> KoID {
|
|
||||||
self.peer.upgrade().map(|p| p.id()).unwrap_or(0)
|
|
||||||
}
|
|
||||||
);
|
|
||||||
// ANCHOR: Channel
|
|
||||||
|
|
||||||
impl Channel {
|
|
||||||
// ANCHOR: Create
|
|
||||||
/// Create a channel and return a pair of its endpoints
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
pub fn create() -> (Arc<Self>, Arc<Self>) {
|
|
||||||
let mut channel0 = Arc::new(Channel {
|
|
||||||
base: KObjectBase::default(),
|
|
||||||
peer: Weak::default(),
|
|
||||||
recv_queue: Default::default(),
|
|
||||||
next_txid: AtomicU32::new(0x8000_0000),
|
|
||||||
});
|
|
||||||
let channel1 = Arc::new(Channel {
|
|
||||||
base: KObjectBase::default(),
|
|
||||||
peer: Arc::downgrade(&channel0),
|
|
||||||
recv_queue: Default::default(),
|
|
||||||
next_txid: AtomicU32::new(0x8000_0000),
|
|
||||||
});
|
|
||||||
// no other reference of `channel0`
|
|
||||||
unsafe {
|
|
||||||
Arc::get_mut_unchecked(&mut channel0).peer = Arc::downgrade(&channel1);
|
|
||||||
}
|
|
||||||
(channel0, channel1)
|
|
||||||
}
|
|
||||||
// ANCHOR_END: Create
|
|
||||||
|
|
||||||
// ANCHOR: Read_Write
|
|
||||||
/// Read a packet from the channel if check is ok, otherwise the msg will keep.
|
|
||||||
pub fn read(&self) -> ZxResult<T> {
|
|
||||||
let mut recv_queue = self.recv_queue.lock();
|
|
||||||
if let Some(_msg) = recv_queue.front() {
|
|
||||||
let msg = recv_queue.pop_front().unwrap();
|
|
||||||
return Ok(msg);
|
|
||||||
}
|
|
||||||
if self.peer_closed() {
|
|
||||||
Err(ZxError::PEER_CLOSED)
|
|
||||||
} else {
|
|
||||||
Err(ZxError::SHOULD_WAIT)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write a packet to the channel
|
|
||||||
pub fn write(&self, msg: T) -> ZxResult {
|
|
||||||
let peer = self.peer.upgrade().ok_or(ZxError::PEER_CLOSED)?;
|
|
||||||
peer.push_general(msg);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Push a message to general queue, called from peer.
|
|
||||||
fn push_general(&self, msg: T) {
|
|
||||||
let mut send_queue = self.recv_queue.lock();
|
|
||||||
send_queue.push_back(msg);
|
|
||||||
}
|
|
||||||
// ANCHOR_END: Read_Write
|
|
||||||
|
|
||||||
/// Generate a new transaction ID.
|
|
||||||
fn new_txid(&self) -> TxID {
|
|
||||||
self.next_txid.fetch_add(1, Ordering::SeqCst)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Is peer channel closed?
|
|
||||||
fn peer_closed(&self) -> bool {
|
|
||||||
self.peer.strong_count() == 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ANCHOR: MP
|
|
||||||
/// The message transferred in the channel.
|
|
||||||
/// See [Channel](struct.Channel.html) for details.
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct MessagePacket {
|
|
||||||
/// The transition id of the message packet
|
|
||||||
pub txid: TxID,
|
|
||||||
/// The data carried by the message packet
|
|
||||||
pub data: Vec<u8>,
|
|
||||||
/// See [Channel](struct.Channel.html) for details.
|
|
||||||
pub handles: Vec<Handle>,
|
|
||||||
}
|
|
||||||
// ANCHOR_END: MP
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_basics() {
|
|
||||||
let (end0, end1) = Channel::create();
|
|
||||||
assert!(Arc::ptr_eq(
|
|
||||||
&end0.peer().unwrap().downcast_arc().unwrap(),
|
|
||||||
&end1
|
|
||||||
));
|
|
||||||
assert_eq!(end0.related_koid(), end1.id());
|
|
||||||
|
|
||||||
drop(end1);
|
|
||||||
assert_eq!(end0.peer().unwrap_err(), ZxError::PEER_CLOSED);
|
|
||||||
assert_eq!(end0.related_koid(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_write() {
|
|
||||||
let (channel0, channel1) = Channel::create();
|
|
||||||
// write a message to each other
|
|
||||||
let txid0 = channel0.new_txid();
|
|
||||||
channel0
|
|
||||||
.write(MessagePacket {
|
|
||||||
txid: txid0,
|
|
||||||
data: Vec::from("hello 1"),
|
|
||||||
handles: Vec::new(),
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
let txid1 = channel1.new_txid();
|
|
||||||
channel1
|
|
||||||
.write(MessagePacket {
|
|
||||||
txid: txid1,
|
|
||||||
data: Vec::from("hello 0"),
|
|
||||||
handles: Vec::new(),
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// read message should success
|
|
||||||
let recv_msg = channel1.read().unwrap();
|
|
||||||
assert_eq!(recv_msg.txid, txid0);
|
|
||||||
assert_eq!(recv_msg.data.as_slice(), b"hello 1");
|
|
||||||
assert!(recv_msg.handles.is_empty());
|
|
||||||
|
|
||||||
let recv_msg = channel0.read().unwrap();
|
|
||||||
assert_eq!(recv_msg.txid, txid1);
|
|
||||||
assert_eq!(recv_msg.data.as_slice(), b"hello 0");
|
|
||||||
assert!(recv_msg.handles.is_empty());
|
|
||||||
|
|
||||||
// read more message should fail.
|
|
||||||
assert_eq!(channel0.read().err(), Some(ZxError::SHOULD_WAIT));
|
|
||||||
assert_eq!(channel1.read().err(), Some(ZxError::SHOULD_WAIT));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn peer_closed() {
|
|
||||||
let (channel0, channel1) = Channel::create();
|
|
||||||
// write a message from peer, then drop it
|
|
||||||
channel1.write(MessagePacket::default()).unwrap();
|
|
||||||
drop(channel1);
|
|
||||||
// read the first message should success.
|
|
||||||
channel0.read().unwrap();
|
|
||||||
// read more message should fail.
|
|
||||||
assert_eq!(channel0.read().err(), Some(ZxError::PEER_CLOSED));
|
|
||||||
// write message should fail.
|
|
||||||
assert_eq!(
|
|
||||||
channel0.write(MessagePacket::default()),
|
|
||||||
Err(ZxError::PEER_CLOSED)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
use super::*;
|
|
||||||
|
|
||||||
mod channel;
|
|
||||||
pub use self::channel::*;
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
#![deny(unused_imports)]
|
|
||||||
#![feature(get_mut_unchecked)]
|
|
||||||
|
|
||||||
extern crate alloc;
|
|
||||||
|
|
||||||
mod error;
|
|
||||||
mod ipc;
|
|
||||||
mod object;
|
|
||||||
mod task;
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
// ANCHOR: handle
|
|
||||||
use super::{KernelObject, Rights};
|
|
||||||
use alloc::sync::Arc;
|
|
||||||
|
|
||||||
/// 内核对象句柄
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Handle {
|
|
||||||
pub object: Arc<dyn KernelObject>,
|
|
||||||
pub rights: Rights,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Handle {
|
|
||||||
/// 创建一个新句柄
|
|
||||||
pub fn new(object: Arc<dyn KernelObject>, rights: Rights) -> Self {
|
|
||||||
Handle { object, rights }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ANCHOR_END: handle
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::object::DummyObject;
|
|
||||||
#[test]
|
|
||||||
fn new_obj_handle() {
|
|
||||||
let obj = DummyObject::new();
|
|
||||||
let handle1 = Handle::new(obj.clone(), Rights::BASIC);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
use super::*;
|
|
||||||
|
|
||||||
mod process;
|
|
||||||
@ -1,119 +0,0 @@
|
|||||||
use super::*;
|
|
||||||
use crate::error::*;
|
|
||||||
use crate::object::*;
|
|
||||||
use alloc::collections::BTreeMap;
|
|
||||||
use alloc::sync::Arc;
|
|
||||||
use spin::Mutex;
|
|
||||||
|
|
||||||
// ANCHOR: process
|
|
||||||
/// 进程对象
|
|
||||||
pub struct Process {
|
|
||||||
base: KObjectBase,
|
|
||||||
inner: Mutex<ProcessInner>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_kobject!(Process);
|
|
||||||
|
|
||||||
struct ProcessInner {
|
|
||||||
handles: BTreeMap<HandleValue, Handle>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type HandleValue = u32;
|
|
||||||
|
|
||||||
impl Process {
|
|
||||||
/// 创建一个新的进程对象
|
|
||||||
pub fn new() -> Arc<Self> {
|
|
||||||
Arc::new(Process {
|
|
||||||
base: KObjectBase::default(),
|
|
||||||
inner: Mutex::new(ProcessInner {
|
|
||||||
handles: BTreeMap::default(),
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// ANCHOR_END: process
|
|
||||||
|
|
||||||
// ANCHOR: add_remove_handle
|
|
||||||
/// 添加一个新的对象句柄
|
|
||||||
pub fn add_handle(&self, handle: Handle) -> HandleValue {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
let value = (0 as HandleValue..)
|
|
||||||
.find(|idx| !inner.handles.contains_key(idx))
|
|
||||||
.unwrap();
|
|
||||||
inner.handles.insert(value, handle);
|
|
||||||
value
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 删除一个对象句柄
|
|
||||||
pub fn remove_handle(&self, handle_value: HandleValue) {
|
|
||||||
self.inner.lock().handles.remove(&handle_value);
|
|
||||||
}
|
|
||||||
// ANCHOR_END: add_remove_handle
|
|
||||||
|
|
||||||
// ANCHOR: get_object_with_rights
|
|
||||||
/// 根据句柄值查找内核对象,并检查权限
|
|
||||||
pub fn get_object_with_rights<T: KernelObject>(
|
|
||||||
&self,
|
|
||||||
handle_value: HandleValue,
|
|
||||||
desired_rights: Rights,
|
|
||||||
) -> ZxResult<Arc<T>> {
|
|
||||||
let handle = self
|
|
||||||
.inner
|
|
||||||
.lock()
|
|
||||||
.handles
|
|
||||||
.get(&handle_value)
|
|
||||||
.ok_or(ZxError::BAD_HANDLE)?
|
|
||||||
.clone();
|
|
||||||
// check type before rights
|
|
||||||
let object = handle
|
|
||||||
.object
|
|
||||||
.downcast_arc::<T>()
|
|
||||||
.map_err(|_| ZxError::WRONG_TYPE)?;
|
|
||||||
if !handle.rights.contains(desired_rights) {
|
|
||||||
return Err(ZxError::ACCESS_DENIED);
|
|
||||||
}
|
|
||||||
Ok(object)
|
|
||||||
}
|
|
||||||
// ANCHOR_END: get_object_with_rights
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use alloc::format;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn new_proc() {
|
|
||||||
let proc = Process::new();
|
|
||||||
assert_eq!(proc.type_name(), "Process");
|
|
||||||
assert_eq!(proc.name(), "");
|
|
||||||
proc.set_name("proc1");
|
|
||||||
assert_eq!(proc.name(), "proc1");
|
|
||||||
assert_eq!(
|
|
||||||
format!("{:?}", proc),
|
|
||||||
format!("Process({}, \"proc1\")", proc.id())
|
|
||||||
);
|
|
||||||
|
|
||||||
let obj: Arc<dyn KernelObject> = proc;
|
|
||||||
assert_eq!(obj.type_name(), "Process");
|
|
||||||
assert_eq!(obj.name(), "proc1");
|
|
||||||
obj.set_name("proc2");
|
|
||||||
assert_eq!(obj.name(), "proc2");
|
|
||||||
assert_eq!(
|
|
||||||
format!("{:?}", obj),
|
|
||||||
format!("Process({}, \"proc2\")", obj.id())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn proc_handle() {
|
|
||||||
let proc = Process::new();
|
|
||||||
let handle = Handle::new(proc.clone(), Rights::DEFAULT_PROCESS);
|
|
||||||
let handle_value = proc.add_handle(handle);
|
|
||||||
|
|
||||||
let object1: Arc<Process> = proc
|
|
||||||
.get_object_with_rights(handle_value, Rights::DEFAULT_PROCESS)
|
|
||||||
.expect("failed to get object");
|
|
||||||
assert!(Arc::ptr_eq(&object1, &proc));
|
|
||||||
|
|
||||||
proc.remove_handle(handle_value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "ch02-02"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Runji Wang <wangrunji0408@163.com>"]
|
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
spin = "0.7"
|
|
||||||
downcast-rs = { version = "1.2.0", default-features = false }
|
|
||||||
bitflags = "1.2"
|
|
||||||
hashbrown = "0.9"
|
|
||||||
@ -1,232 +0,0 @@
|
|||||||
// ANCHOR: result
|
|
||||||
///
|
|
||||||
pub type ZxResult<T = ()> = Result<T, ZxError>;
|
|
||||||
// ANCHOR_END: result
|
|
||||||
|
|
||||||
// ANCHOR: error_begin
|
|
||||||
/// Zircon statuses are signed 32 bit integers. The space of values is
|
|
||||||
/// divided as follows:
|
|
||||||
/// - The zero value is for the OK status.
|
|
||||||
/// - Negative values are defined by the system, in this file.
|
|
||||||
/// - Positive values are reserved for protocol-specific error values,
|
|
||||||
/// and will never be defined by the system.
|
|
||||||
#[allow(non_camel_case_types, dead_code)]
|
|
||||||
#[repr(i32)]
|
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
|
||||||
pub enum ZxError {
|
|
||||||
OK = 0,
|
|
||||||
|
|
||||||
// ======= Internal failures =======
|
|
||||||
/// The system encountered an otherwise unspecified error
|
|
||||||
/// while performing the operation.
|
|
||||||
INTERNAL = -1,
|
|
||||||
|
|
||||||
/// The operation is not implemented, supported,
|
|
||||||
/// or enabled.
|
|
||||||
NOT_SUPPORTED = -2,
|
|
||||||
// ANCHOR_END: error_begin
|
|
||||||
/// The system was not able to allocate some resource
|
|
||||||
/// needed for the operation.
|
|
||||||
NO_RESOURCES = -3,
|
|
||||||
|
|
||||||
/// The system was not able to allocate memory needed
|
|
||||||
/// for the operation.
|
|
||||||
NO_MEMORY = -4,
|
|
||||||
|
|
||||||
// -5 used to be ZX_ERR_CALL_FAILED.
|
|
||||||
/// The system call was interrupted, but should be
|
|
||||||
/// retried. This should not be seen outside of the VDSO.
|
|
||||||
INTERNAL_INTR_RETRY = -6,
|
|
||||||
|
|
||||||
// ======= Parameter errors =======
|
|
||||||
/// an argument is invalid, ex. null pointer
|
|
||||||
INVALID_ARGS = -10,
|
|
||||||
|
|
||||||
/// A specified handle value does not refer to a handle.
|
|
||||||
BAD_HANDLE = -11,
|
|
||||||
|
|
||||||
/// The subject of the operation is the wrong type to
|
|
||||||
/// perform the operation.
|
|
||||||
/// Example: Attempting a message_read on a thread handle.
|
|
||||||
WRONG_TYPE = -12,
|
|
||||||
|
|
||||||
/// The specified syscall number is invalid.
|
|
||||||
BAD_SYSCALL = -13,
|
|
||||||
|
|
||||||
/// An argument is outside the valid range for this
|
|
||||||
/// operation.
|
|
||||||
OUT_OF_RANGE = -14,
|
|
||||||
|
|
||||||
/// A caller provided buffer is too small for
|
|
||||||
/// this operation.
|
|
||||||
BUFFER_TOO_SMALL = -15,
|
|
||||||
|
|
||||||
// ======= Precondition or state errors =======
|
|
||||||
/// operation failed because the current state of the
|
|
||||||
/// object does not allow it, or a precondition of the operation is
|
|
||||||
/// not satisfied
|
|
||||||
BAD_STATE = -20,
|
|
||||||
|
|
||||||
/// The time limit for the operation elapsed before
|
|
||||||
/// the operation completed.
|
|
||||||
TIMED_OUT = -21,
|
|
||||||
|
|
||||||
/// The operation cannot be performed currently but
|
|
||||||
/// potentially could succeed if the caller waits for a prerequisite
|
|
||||||
/// to be satisfied, for example waiting for a handle to be readable
|
|
||||||
/// or writable.
|
|
||||||
/// Example: Attempting to read from a channel that has no
|
|
||||||
/// messages waiting but has an open remote will return ZX_ERR_SHOULD_WAIT.
|
|
||||||
/// Attempting to read from a channel that has no messages waiting
|
|
||||||
/// and has a closed remote end will return ZX_ERR_PEER_CLOSED.
|
|
||||||
SHOULD_WAIT = -22,
|
|
||||||
|
|
||||||
/// The in-progress operation (e.g. a wait) has been
|
|
||||||
/// canceled.
|
|
||||||
CANCELED = -23,
|
|
||||||
|
|
||||||
/// The operation failed because the remote end of the
|
|
||||||
/// subject of the operation was closed.
|
|
||||||
PEER_CLOSED = -24,
|
|
||||||
|
|
||||||
/// The requested entity is not found.
|
|
||||||
NOT_FOUND = -25,
|
|
||||||
|
|
||||||
/// An object with the specified identifier
|
|
||||||
/// already exists.
|
|
||||||
/// Example: Attempting to create a file when a file already exists
|
|
||||||
/// with that name.
|
|
||||||
ALREADY_EXISTS = -26,
|
|
||||||
|
|
||||||
/// The operation failed because the named entity
|
|
||||||
/// is already owned or controlled by another entity. The operation
|
|
||||||
/// could succeed later if the current owner releases the entity.
|
|
||||||
ALREADY_BOUND = -27,
|
|
||||||
|
|
||||||
/// The subject of the operation is currently unable
|
|
||||||
/// to perform the operation.
|
|
||||||
/// Note: This is used when there's no direct way for the caller to
|
|
||||||
/// observe when the subject will be able to perform the operation
|
|
||||||
/// and should thus retry.
|
|
||||||
UNAVAILABLE = -28,
|
|
||||||
|
|
||||||
// ======= Permission check errors =======
|
|
||||||
/// The caller did not have permission to perform
|
|
||||||
/// the specified operation.
|
|
||||||
ACCESS_DENIED = -30,
|
|
||||||
|
|
||||||
// ======= Input-output errors =======
|
|
||||||
/// Otherwise unspecified error occurred during I/O.
|
|
||||||
IO = -40,
|
|
||||||
|
|
||||||
/// The entity the I/O operation is being performed on
|
|
||||||
/// rejected the operation.
|
|
||||||
/// Example: an I2C device NAK'ing a transaction or a disk controller
|
|
||||||
/// rejecting an invalid command, or a stalled USB endpoint.
|
|
||||||
IO_REFUSED = -41,
|
|
||||||
|
|
||||||
/// The data in the operation failed an integrity
|
|
||||||
/// check and is possibly corrupted.
|
|
||||||
/// Example: CRC or Parity error.
|
|
||||||
IO_DATA_INTEGRITY = -42,
|
|
||||||
|
|
||||||
/// The data in the operation is currently unavailable
|
|
||||||
/// and may be permanently lost.
|
|
||||||
/// Example: A disk block is irrecoverably damaged.
|
|
||||||
IO_DATA_LOSS = -43,
|
|
||||||
|
|
||||||
/// The device is no longer available (has been
|
|
||||||
/// unplugged from the system, powered down, or the driver has been
|
|
||||||
/// unloaded,
|
|
||||||
IO_NOT_PRESENT = -44,
|
|
||||||
|
|
||||||
/// More data was received from the device than expected.
|
|
||||||
/// Example: a USB "babble" error due to a device sending more data than
|
|
||||||
/// the host queued to receive.
|
|
||||||
IO_OVERRUN = -45,
|
|
||||||
|
|
||||||
/// An operation did not complete within the required timeframe.
|
|
||||||
/// Example: A USB isochronous transfer that failed to complete due to an overrun or underrun.
|
|
||||||
IO_MISSED_DEADLINE = -46,
|
|
||||||
|
|
||||||
/// The data in the operation is invalid parameter or is out of range.
|
|
||||||
/// Example: A USB transfer that failed to complete with TRB Error
|
|
||||||
IO_INVALID = -47,
|
|
||||||
|
|
||||||
// ======== Filesystem Errors ========
|
|
||||||
/// Path name is too long.
|
|
||||||
BAD_PATH = -50,
|
|
||||||
|
|
||||||
/// Object is not a directory or does not support
|
|
||||||
/// directory operations.
|
|
||||||
/// Example: Attempted to open a file as a directory or
|
|
||||||
/// attempted to do directory operations on a file.
|
|
||||||
NOT_DIR = -51,
|
|
||||||
|
|
||||||
/// Object is not a regular file.
|
|
||||||
NOT_FILE = -52,
|
|
||||||
|
|
||||||
/// This operation would cause a file to exceed a
|
|
||||||
/// filesystem-specific size limit
|
|
||||||
FILE_BIG = -53,
|
|
||||||
|
|
||||||
/// Filesystem or device space is exhausted.
|
|
||||||
NO_SPACE = -54,
|
|
||||||
|
|
||||||
/// Directory is not empty.
|
|
||||||
NOT_EMPTY = -55,
|
|
||||||
|
|
||||||
// ======== Flow Control ========
|
|
||||||
// These are not errors, as such, and will never be returned
|
|
||||||
// by a syscall or public API. They exist to allow callbacks
|
|
||||||
// to request changes in operation.
|
|
||||||
/// Do not call again.
|
|
||||||
/// Example: A notification callback will be called on every
|
|
||||||
/// event until it returns something other than ZX_OK.
|
|
||||||
/// This status allows differentiation between "stop due to
|
|
||||||
/// an error" and "stop because the work is done."
|
|
||||||
STOP = -60,
|
|
||||||
|
|
||||||
/// Advance to the next item.
|
|
||||||
/// Example: A notification callback will use this response
|
|
||||||
/// to indicate it did not "consume" an item passed to it,
|
|
||||||
/// but by choice, not due to an error condition.
|
|
||||||
NEXT = -61,
|
|
||||||
|
|
||||||
/// Ownership of the item has moved to an asynchronous worker.
|
|
||||||
///
|
|
||||||
/// Unlike ZX_ERR_STOP, which implies that iteration on an object
|
|
||||||
/// should stop, and ZX_ERR_NEXT, which implies that iteration
|
|
||||||
/// should continue to the next item, ZX_ERR_ASYNC implies
|
|
||||||
/// that an asynchronous worker is responsible for continuing iteration.
|
|
||||||
///
|
|
||||||
/// Example: A notification callback will be called on every
|
|
||||||
/// event, but one event needs to handle some work asynchronously
|
|
||||||
/// before it can continue. ZX_ERR_ASYNC implies the worker is
|
|
||||||
/// responsible for resuming iteration once its work has completed.
|
|
||||||
ASYNC = -62,
|
|
||||||
|
|
||||||
// ======== Network-related errors ========
|
|
||||||
/// Specified protocol is not
|
|
||||||
/// supported.
|
|
||||||
PROTOCOL_NOT_SUPPORTED = -70,
|
|
||||||
|
|
||||||
/// Host is unreachable.
|
|
||||||
ADDRESS_UNREACHABLE = -71,
|
|
||||||
|
|
||||||
/// Address is being used by someone else.
|
|
||||||
ADDRESS_IN_USE = -72,
|
|
||||||
|
|
||||||
/// Socket is not connected.
|
|
||||||
NOT_CONNECTED = -73,
|
|
||||||
|
|
||||||
/// Remote peer rejected the connection.
|
|
||||||
CONNECTION_REFUSED = -74,
|
|
||||||
|
|
||||||
/// Connection was reset.
|
|
||||||
CONNECTION_RESET = -75,
|
|
||||||
// ANCHOR: error_end
|
|
||||||
/// Connection was aborted.
|
|
||||||
CONNECTION_ABORTED = -76,
|
|
||||||
}
|
|
||||||
// ANCHOR_END: error_end
|
|
||||||
@ -1,176 +0,0 @@
|
|||||||
use {
|
|
||||||
super::*,
|
|
||||||
crate::error::*,
|
|
||||||
crate::object::*,
|
|
||||||
alloc::collections::VecDeque,
|
|
||||||
alloc::sync::{Arc, Weak},
|
|
||||||
alloc::vec::Vec,
|
|
||||||
core::sync::atomic::{AtomicU32, Ordering},
|
|
||||||
spin::Mutex,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct Channel {
|
|
||||||
base: KObjectBase,
|
|
||||||
peer: Weak<Channel>,
|
|
||||||
recv_queue: Mutex<VecDeque<T>>,
|
|
||||||
next_txid: AtomicU32,
|
|
||||||
}
|
|
||||||
|
|
||||||
type T = MessagePacket;
|
|
||||||
type TxID = u32;
|
|
||||||
|
|
||||||
impl_kobject!(Channel
|
|
||||||
fn peer(&self) -> ZxResult<Arc<dyn KernelObject>> {
|
|
||||||
let peer = self.peer.upgrade().ok_or(ZxError::PEER_CLOSED)?;
|
|
||||||
Ok(peer)
|
|
||||||
}
|
|
||||||
fn related_koid(&self) -> KoID {
|
|
||||||
self.peer.upgrade().map(|p| p.id()).unwrap_or(0)
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
impl Channel {
|
|
||||||
/// Create a channel and return a pair of its endpoints
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
pub fn create() -> (Arc<Self>, Arc<Self>) {
|
|
||||||
let mut channel0 = Arc::new(Channel {
|
|
||||||
base: KObjectBase::default(),
|
|
||||||
peer: Weak::default(),
|
|
||||||
recv_queue: Default::default(),
|
|
||||||
next_txid: AtomicU32::new(0x8000_0000),
|
|
||||||
});
|
|
||||||
let channel1 = Arc::new(Channel {
|
|
||||||
base: KObjectBase::default(),
|
|
||||||
peer: Arc::downgrade(&channel0),
|
|
||||||
recv_queue: Default::default(),
|
|
||||||
next_txid: AtomicU32::new(0x8000_0000),
|
|
||||||
});
|
|
||||||
// no other reference of `channel0`
|
|
||||||
unsafe {
|
|
||||||
Arc::get_mut_unchecked(&mut channel0).peer = Arc::downgrade(&channel1);
|
|
||||||
}
|
|
||||||
(channel0, channel1)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read a packet from the channel if check is ok, otherwise the msg will keep.
|
|
||||||
pub fn read(&self) -> ZxResult<T> {
|
|
||||||
let mut recv_queue = self.recv_queue.lock();
|
|
||||||
if recv_queue.front().is_some() {
|
|
||||||
let msg = recv_queue.pop_front().unwrap();
|
|
||||||
return Ok(msg);
|
|
||||||
}
|
|
||||||
if self.peer_closed() {
|
|
||||||
Err(ZxError::PEER_CLOSED)
|
|
||||||
} else {
|
|
||||||
Err(ZxError::SHOULD_WAIT)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write a packet to the channel
|
|
||||||
pub fn write(&self, msg: T) -> ZxResult {
|
|
||||||
let peer = self.peer.upgrade().ok_or(ZxError::PEER_CLOSED)?;
|
|
||||||
peer.push_general(msg);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Push a message to general queue, called from peer.
|
|
||||||
fn push_general(&self, msg: T) {
|
|
||||||
let mut send_queue = self.recv_queue.lock();
|
|
||||||
send_queue.push_back(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generate a new transaction ID for `call`.
|
|
||||||
fn new_txid(&self) -> TxID {
|
|
||||||
self.next_txid.fetch_add(1, Ordering::SeqCst)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Is peer channel closed?
|
|
||||||
fn peer_closed(&self) -> bool {
|
|
||||||
self.peer.strong_count() == 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The message transferred in the channel.
|
|
||||||
/// See [Channel](struct.Channel.html) for details.
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct MessagePacket {
|
|
||||||
/// The transition id of the message packet
|
|
||||||
pub txid: TxID,
|
|
||||||
/// The data carried by the message packet
|
|
||||||
pub data: Vec<u8>,
|
|
||||||
/// See [Channel](struct.Channel.html) for details.
|
|
||||||
pub handles: Vec<Handle>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_basics() {
|
|
||||||
let (end0, end1) = Channel::create();
|
|
||||||
assert!(Arc::ptr_eq(
|
|
||||||
&end0.peer().unwrap().downcast_arc().unwrap(),
|
|
||||||
&end1
|
|
||||||
));
|
|
||||||
assert_eq!(end0.related_koid(), end1.id());
|
|
||||||
|
|
||||||
drop(end1);
|
|
||||||
assert_eq!(end0.peer().unwrap_err(), ZxError::PEER_CLOSED);
|
|
||||||
assert_eq!(end0.related_koid(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_write() {
|
|
||||||
let (channel0, channel1) = Channel::create();
|
|
||||||
// write a message to each other
|
|
||||||
let txid0 = channel0.new_txid();
|
|
||||||
channel0
|
|
||||||
.write(MessagePacket {
|
|
||||||
txid: txid0,
|
|
||||||
data: Vec::from("hello 1"),
|
|
||||||
handles: Vec::new(),
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
let txid1 = channel1.new_txid();
|
|
||||||
channel1
|
|
||||||
.write(MessagePacket {
|
|
||||||
txid: txid1,
|
|
||||||
data: Vec::from("hello 0"),
|
|
||||||
handles: Vec::new(),
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// read message should success
|
|
||||||
let recv_msg = channel1.read().unwrap();
|
|
||||||
assert_eq!(recv_msg.txid, txid0);
|
|
||||||
assert_eq!(recv_msg.data.as_slice(), b"hello 1");
|
|
||||||
assert!(recv_msg.handles.is_empty());
|
|
||||||
|
|
||||||
let recv_msg = channel0.read().unwrap();
|
|
||||||
assert_eq!(recv_msg.txid, txid1);
|
|
||||||
assert_eq!(recv_msg.data.as_slice(), b"hello 0");
|
|
||||||
assert!(recv_msg.handles.is_empty());
|
|
||||||
|
|
||||||
// read more message should fail.
|
|
||||||
assert_eq!(channel0.read().err(), Some(ZxError::SHOULD_WAIT));
|
|
||||||
assert_eq!(channel1.read().err(), Some(ZxError::SHOULD_WAIT));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn peer_closed() {
|
|
||||||
let (channel0, channel1) = Channel::create();
|
|
||||||
// write a message from peer, then drop it
|
|
||||||
channel1.write(MessagePacket::default()).unwrap();
|
|
||||||
drop(channel1);
|
|
||||||
// read the first message should success.
|
|
||||||
channel0.read().unwrap();
|
|
||||||
// read more message should fail.
|
|
||||||
assert_eq!(channel0.read().err(), Some(ZxError::PEER_CLOSED));
|
|
||||||
// write message should fail.
|
|
||||||
assert_eq!(
|
|
||||||
channel0.write(MessagePacket::default()),
|
|
||||||
Err(ZxError::PEER_CLOSED)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
use super::*;
|
|
||||||
|
|
||||||
mod channel;
|
|
||||||
pub use self::channel::*;
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
#![deny(unused_imports)]
|
|
||||||
#![allow(dead_code)]
|
|
||||||
#![feature(get_mut_unchecked)]
|
|
||||||
|
|
||||||
extern crate alloc;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
#[macro_use]
|
|
||||||
extern crate std;
|
|
||||||
|
|
||||||
mod error;
|
|
||||||
mod ipc;
|
|
||||||
mod object;
|
|
||||||
mod task;
|
|
||||||
mod vm;
|
|
||||||
|
|
||||||
pub use self::error::*;
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
// ANCHOR: handle
|
|
||||||
use super::{KernelObject, Rights};
|
|
||||||
use alloc::sync::Arc;
|
|
||||||
|
|
||||||
pub type HandleValue = u32;
|
|
||||||
|
|
||||||
/// 内核对象句柄
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Handle {
|
|
||||||
pub object: Arc<dyn KernelObject>,
|
|
||||||
pub rights: Rights,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Handle {
|
|
||||||
/// 创建一个新句柄
|
|
||||||
pub fn new(object: Arc<dyn KernelObject>, rights: Rights) -> Self {
|
|
||||||
Handle { object, rights }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ANCHOR_END: handle
|
|
||||||
@ -1,408 +0,0 @@
|
|||||||
use {
|
|
||||||
super::job_policy::*,
|
|
||||||
super::process::Process,
|
|
||||||
super::*,
|
|
||||||
crate::error::*,
|
|
||||||
crate::object::*,
|
|
||||||
crate::task::Task,
|
|
||||||
alloc::sync::{Arc, Weak},
|
|
||||||
alloc::vec::Vec,
|
|
||||||
spin::Mutex,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Job 对象
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub struct Job {
|
|
||||||
base: KObjectBase,
|
|
||||||
parent: Option<Arc<Job>>,
|
|
||||||
parent_policy: JobPolicy,
|
|
||||||
inner: Mutex<JobInner>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_kobject!(Job
|
|
||||||
fn get_child(&self, id: KoID) -> ZxResult<Arc<dyn KernelObject>> {
|
|
||||||
let inner = self.inner.lock();
|
|
||||||
if let Some(job) = inner.children.iter().filter_map(|o|o.upgrade()).find(|o| o.id() == id) {
|
|
||||||
return Ok(job);
|
|
||||||
}
|
|
||||||
if let Some(proc) = inner.processes.iter().find(|o| o.id() == id) {
|
|
||||||
return Ok(proc.clone());
|
|
||||||
}
|
|
||||||
Err(ZxError::NOT_FOUND)
|
|
||||||
}
|
|
||||||
fn related_koid(&self) -> KoID {
|
|
||||||
self.parent.as_ref().map(|p| p.id()).unwrap_or(0)
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct JobInner {
|
|
||||||
policy: JobPolicy,
|
|
||||||
children: Vec<Weak<Job>>,
|
|
||||||
processes: Vec<Arc<Process>>,
|
|
||||||
// if the job is killed, no more child creation should works
|
|
||||||
killed: bool,
|
|
||||||
self_ref: Weak<Job>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Job {
|
|
||||||
/// Create the root job.
|
|
||||||
pub fn root() -> Arc<Self> {
|
|
||||||
let job = Arc::new(Job {
|
|
||||||
base: KObjectBase::new(),
|
|
||||||
parent: None,
|
|
||||||
parent_policy: JobPolicy::default(),
|
|
||||||
inner: Mutex::new(JobInner::default()),
|
|
||||||
});
|
|
||||||
job.inner.lock().self_ref = Arc::downgrade(&job);
|
|
||||||
job
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new child job object.
|
|
||||||
pub fn create_child(self: &Arc<Self>) -> ZxResult<Arc<Self>> {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
if inner.killed {
|
|
||||||
return Err(ZxError::BAD_STATE);
|
|
||||||
}
|
|
||||||
let child = Arc::new(Job {
|
|
||||||
base: KObjectBase::new(),
|
|
||||||
parent: Some(self.clone()),
|
|
||||||
parent_policy: inner.policy.merge(&self.parent_policy),
|
|
||||||
inner: Mutex::new(JobInner::default()),
|
|
||||||
});
|
|
||||||
let child_weak = Arc::downgrade(&child);
|
|
||||||
child.inner.lock().self_ref = child_weak.clone();
|
|
||||||
inner.children.push(child_weak);
|
|
||||||
Ok(child)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_child(&self, to_remove: &Weak<Job>) {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
inner.children.retain(|child| !to_remove.ptr_eq(child));
|
|
||||||
if inner.killed && inner.processes.is_empty() && inner.children.is_empty() {
|
|
||||||
drop(inner);
|
|
||||||
self.terminate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the policy of the job.
|
|
||||||
pub fn policy(&self) -> JobPolicy {
|
|
||||||
self.inner.lock().policy.merge(&self.parent_policy)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the parent job.
|
|
||||||
pub fn parent(&self) -> Option<Arc<Self>> {
|
|
||||||
self.parent.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets one or more security and/or resource policies to an empty job.
|
|
||||||
///
|
|
||||||
/// The job's effective policies is the combination of the parent's
|
|
||||||
/// effective policies and the policies specified in policy.
|
|
||||||
///
|
|
||||||
/// After this call succeeds any new child process or child job will have
|
|
||||||
/// the new effective policy applied to it.
|
|
||||||
pub fn set_policy_basic(
|
|
||||||
&self,
|
|
||||||
options: SetPolicyOptions,
|
|
||||||
policies: &[BasicPolicy],
|
|
||||||
) -> ZxResult {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
if !inner.is_empty() {
|
|
||||||
return Err(ZxError::BAD_STATE);
|
|
||||||
}
|
|
||||||
for policy in policies {
|
|
||||||
if self.parent_policy.get_action(policy.condition).is_some() {
|
|
||||||
match options {
|
|
||||||
SetPolicyOptions::Absolute => return Err(ZxError::ALREADY_EXISTS),
|
|
||||||
SetPolicyOptions::Relative => {}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
inner.policy.apply(*policy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a process to the job.
|
|
||||||
pub(super) fn add_process(&self, process: Arc<Process>) -> ZxResult {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
if inner.killed {
|
|
||||||
return Err(ZxError::BAD_STATE);
|
|
||||||
}
|
|
||||||
inner.processes.push(process);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove a process from the job.
|
|
||||||
pub(super) fn remove_process(&self, id: KoID) {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
inner.processes.retain(|proc| proc.id() != id);
|
|
||||||
if inner.killed && inner.processes.is_empty() && inner.children.is_empty() {
|
|
||||||
drop(inner);
|
|
||||||
self.terminate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check whether this job is root job.
|
|
||||||
pub fn check_root_job(&self) -> ZxResult {
|
|
||||||
if self.parent.is_some() {
|
|
||||||
Err(ZxError::ACCESS_DENIED)
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get KoIDs of Processes.
|
|
||||||
pub fn process_ids(&self) -> Vec<KoID> {
|
|
||||||
self.inner.lock().processes.iter().map(|p| p.id()).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get KoIDs of children Jobs.
|
|
||||||
pub fn children_ids(&self) -> Vec<KoID> {
|
|
||||||
self.inner
|
|
||||||
.lock()
|
|
||||||
.children
|
|
||||||
.iter()
|
|
||||||
.filter_map(|j| j.upgrade())
|
|
||||||
.map(|j| j.id())
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return true if this job has no processes and no child jobs.
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.inner.lock().is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The job finally terminates.
|
|
||||||
fn terminate(&self) {
|
|
||||||
if let Some(parent) = self.parent.as_ref() {
|
|
||||||
parent.remove_child(&self.inner.lock().self_ref)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Task for Job {
|
|
||||||
/// Kill the job. The job do not terminate immediately when killed.
|
|
||||||
/// It will terminate after all its children and processes are terminated.
|
|
||||||
fn kill(&self) {
|
|
||||||
let (children, processes) = {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
if inner.killed {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
inner.killed = true;
|
|
||||||
(inner.children.clone(), inner.processes.clone())
|
|
||||||
};
|
|
||||||
if children.is_empty() && processes.is_empty() {
|
|
||||||
self.terminate();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for child in children {
|
|
||||||
if let Some(child) = child.upgrade() {
|
|
||||||
child.kill();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for proc in processes {
|
|
||||||
proc.kill();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn suspend(&self) {
|
|
||||||
panic!("job do not support suspend");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resume(&self) {
|
|
||||||
panic!("job do not support resume");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl JobInner {
|
|
||||||
fn is_empty(&self) -> bool {
|
|
||||||
self.processes.is_empty() && self.children.is_empty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Job {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.terminate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::task::TASK_RETCODE_SYSCALL_KILL;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn create() {
|
|
||||||
let root_job = Job::root();
|
|
||||||
let job = Job::create_child(&root_job).expect("failed to create job");
|
|
||||||
|
|
||||||
let child = root_job
|
|
||||||
.get_child(job.id())
|
|
||||||
.unwrap()
|
|
||||||
.downcast_arc()
|
|
||||||
.unwrap();
|
|
||||||
assert!(Arc::ptr_eq(&child, &job));
|
|
||||||
assert_eq!(job.related_koid(), root_job.id());
|
|
||||||
assert_eq!(root_job.related_koid(), 0);
|
|
||||||
|
|
||||||
root_job.kill();
|
|
||||||
assert_eq!(root_job.create_child().err(), Some(ZxError::BAD_STATE));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn set_policy() {
|
|
||||||
let root_job = Job::root();
|
|
||||||
|
|
||||||
// default policy
|
|
||||||
assert_eq!(
|
|
||||||
root_job.policy().get_action(PolicyCondition::BadHandle),
|
|
||||||
None
|
|
||||||
);
|
|
||||||
|
|
||||||
// set policy for root job
|
|
||||||
let policy = &[BasicPolicy {
|
|
||||||
condition: PolicyCondition::BadHandle,
|
|
||||||
action: PolicyAction::Deny,
|
|
||||||
}];
|
|
||||||
root_job
|
|
||||||
.set_policy_basic(SetPolicyOptions::Relative, policy)
|
|
||||||
.expect("failed to set policy");
|
|
||||||
assert_eq!(
|
|
||||||
root_job.policy().get_action(PolicyCondition::BadHandle),
|
|
||||||
Some(PolicyAction::Deny)
|
|
||||||
);
|
|
||||||
|
|
||||||
// override policy should success
|
|
||||||
let policy = &[BasicPolicy {
|
|
||||||
condition: PolicyCondition::BadHandle,
|
|
||||||
action: PolicyAction::Allow,
|
|
||||||
}];
|
|
||||||
root_job
|
|
||||||
.set_policy_basic(SetPolicyOptions::Relative, policy)
|
|
||||||
.expect("failed to set policy");
|
|
||||||
assert_eq!(
|
|
||||||
root_job.policy().get_action(PolicyCondition::BadHandle),
|
|
||||||
Some(PolicyAction::Allow)
|
|
||||||
);
|
|
||||||
|
|
||||||
// create a child job
|
|
||||||
let job = Job::create_child(&root_job).expect("failed to create job");
|
|
||||||
|
|
||||||
// should inherit parent's policy.
|
|
||||||
assert_eq!(
|
|
||||||
job.policy().get_action(PolicyCondition::BadHandle),
|
|
||||||
Some(PolicyAction::Allow)
|
|
||||||
);
|
|
||||||
|
|
||||||
// setting policy for a non-empty job should fail.
|
|
||||||
assert_eq!(
|
|
||||||
root_job.set_policy_basic(SetPolicyOptions::Relative, &[]),
|
|
||||||
Err(ZxError::BAD_STATE)
|
|
||||||
);
|
|
||||||
|
|
||||||
// set new policy should success.
|
|
||||||
let policy = &[BasicPolicy {
|
|
||||||
condition: PolicyCondition::WrongObject,
|
|
||||||
action: PolicyAction::Allow,
|
|
||||||
}];
|
|
||||||
job.set_policy_basic(SetPolicyOptions::Relative, policy)
|
|
||||||
.expect("failed to set policy");
|
|
||||||
assert_eq!(
|
|
||||||
job.policy().get_action(PolicyCondition::WrongObject),
|
|
||||||
Some(PolicyAction::Allow)
|
|
||||||
);
|
|
||||||
|
|
||||||
// relatively setting existing policy should be ignored.
|
|
||||||
let policy = &[BasicPolicy {
|
|
||||||
condition: PolicyCondition::BadHandle,
|
|
||||||
action: PolicyAction::Deny,
|
|
||||||
}];
|
|
||||||
job.set_policy_basic(SetPolicyOptions::Relative, policy)
|
|
||||||
.expect("failed to set policy");
|
|
||||||
assert_eq!(
|
|
||||||
job.policy().get_action(PolicyCondition::BadHandle),
|
|
||||||
Some(PolicyAction::Allow)
|
|
||||||
);
|
|
||||||
|
|
||||||
// absolutely setting existing policy should fail.
|
|
||||||
assert_eq!(
|
|
||||||
job.set_policy_basic(SetPolicyOptions::Absolute, policy),
|
|
||||||
Err(ZxError::ALREADY_EXISTS)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parent_child() {
|
|
||||||
let root_job = Job::root();
|
|
||||||
let job = Job::create_child(&root_job).expect("failed to create job");
|
|
||||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
|
||||||
|
|
||||||
assert_eq!(root_job.get_child(job.id()).unwrap().id(), job.id());
|
|
||||||
assert_eq!(root_job.get_child(proc.id()).unwrap().id(), proc.id());
|
|
||||||
assert_eq!(
|
|
||||||
root_job.get_child(root_job.id()).err(),
|
|
||||||
Some(ZxError::NOT_FOUND)
|
|
||||||
);
|
|
||||||
assert!(Arc::ptr_eq(&job.parent().unwrap(), &root_job));
|
|
||||||
|
|
||||||
let job1 = root_job.create_child().expect("failed to create job");
|
|
||||||
let proc1 = Process::create(&root_job, "proc1").expect("failed to create process");
|
|
||||||
assert_eq!(root_job.children_ids(), vec![job.id(), job1.id()]);
|
|
||||||
assert_eq!(root_job.process_ids(), vec![proc.id(), proc1.id()]);
|
|
||||||
|
|
||||||
root_job.kill();
|
|
||||||
assert_eq!(root_job.create_child().err(), Some(ZxError::BAD_STATE));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn check() {
|
|
||||||
let root_job = Job::root();
|
|
||||||
assert!(root_job.is_empty());
|
|
||||||
let job = root_job.create_child().expect("failed to create job");
|
|
||||||
assert_eq!(root_job.check_root_job(), Ok(()));
|
|
||||||
assert_eq!(job.check_root_job(), Err(ZxError::ACCESS_DENIED));
|
|
||||||
|
|
||||||
assert!(!root_job.is_empty());
|
|
||||||
assert!(job.is_empty());
|
|
||||||
|
|
||||||
let _proc = Process::create(&job, "proc").expect("failed to create process");
|
|
||||||
assert!(!job.is_empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn kill() {
|
|
||||||
let root_job = Job::root();
|
|
||||||
let job = Job::create_child(&root_job).expect("failed to create job");
|
|
||||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
|
||||||
let thread = Thread::create(&proc, "thread").expect("failed to create thread");
|
|
||||||
|
|
||||||
root_job.kill();
|
|
||||||
assert!(root_job.inner.lock().killed);
|
|
||||||
assert!(job.inner.lock().killed);
|
|
||||||
assert_eq!(proc.status(), Status::Exited(TASK_RETCODE_SYSCALL_KILL));
|
|
||||||
// assert_eq!(thread.state(), ThreadState::Dying);
|
|
||||||
|
|
||||||
std::mem::drop(thread);
|
|
||||||
assert!(root_job.inner.lock().killed);
|
|
||||||
assert!(job.inner.lock().killed);
|
|
||||||
assert_eq!(proc.status(), Status::Exited(TASK_RETCODE_SYSCALL_KILL));
|
|
||||||
// assert_eq!(thread.state(), ThreadState::Dead);
|
|
||||||
|
|
||||||
// The job has no children.
|
|
||||||
let root_job = Job::root();
|
|
||||||
root_job.kill();
|
|
||||||
assert!(root_job.inner.lock().killed);
|
|
||||||
|
|
||||||
// The job's process have no threads.
|
|
||||||
let root_job = Job::root();
|
|
||||||
let job = Job::create_child(&root_job).expect("failed to create job");
|
|
||||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
|
||||||
root_job.kill();
|
|
||||||
assert!(root_job.inner.lock().killed);
|
|
||||||
assert!(job.inner.lock().killed);
|
|
||||||
assert_eq!(proc.status(), Status::Exited(TASK_RETCODE_SYSCALL_KILL));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,108 +0,0 @@
|
|||||||
/// Security and resource policies of a job.
|
|
||||||
#[derive(Default, Copy, Clone)]
|
|
||||||
pub struct JobPolicy {
|
|
||||||
// TODO: use bitset
|
|
||||||
action: [Option<PolicyAction>; 15],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl JobPolicy {
|
|
||||||
/// Get the action of a policy `condition`.
|
|
||||||
pub fn get_action(&self, condition: PolicyCondition) -> Option<PolicyAction> {
|
|
||||||
self.action[condition as usize]
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Apply a basic policy.
|
|
||||||
pub fn apply(&mut self, policy: BasicPolicy) {
|
|
||||||
self.action[policy.condition as usize] = Some(policy.action);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Merge the policy with `parent`'s.
|
|
||||||
pub fn merge(&self, parent: &Self) -> Self {
|
|
||||||
let mut new = *self;
|
|
||||||
for i in 0..15 {
|
|
||||||
if parent.action[i].is_some() {
|
|
||||||
new.action[i] = parent.action[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
new
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Control the effect in the case of conflict between
|
|
||||||
/// the existing policies and the new policies when setting new policies.
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub enum SetPolicyOptions {
|
|
||||||
/// Policy is applied for all conditions in policy or the call fails.
|
|
||||||
Absolute,
|
|
||||||
/// Policy is applied for the conditions not specifically overridden by the parent policy.
|
|
||||||
Relative,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The policy type.
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub struct BasicPolicy {
|
|
||||||
/// Condition when the policy is applied.
|
|
||||||
pub condition: PolicyCondition,
|
|
||||||
///
|
|
||||||
pub action: PolicyAction,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The condition when a policy is applied.
|
|
||||||
#[repr(u32)]
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub enum PolicyCondition {
|
|
||||||
/// A process under this job is attempting to issue a syscall with an invalid handle.
|
|
||||||
/// In this case, `PolicyAction::Allow` and `PolicyAction::Deny` are equivalent:
|
|
||||||
/// if the syscall returns, it will always return the error ZX_ERR_BAD_HANDLE.
|
|
||||||
BadHandle = 0,
|
|
||||||
/// A process under this job is attempting to issue a syscall with a handle that does not support such operation.
|
|
||||||
WrongObject = 1,
|
|
||||||
/// A process under this job is attempting to map an address region with write-execute access.
|
|
||||||
VmarWx = 2,
|
|
||||||
/// A special condition that stands for all of the above ZX_NEW conditions
|
|
||||||
/// such as NEW_VMO, NEW_CHANNEL, NEW_EVENT, NEW_EVENTPAIR, NEW_PORT, NEW_SOCKET, NEW_FIFO,
|
|
||||||
/// And any future ZX_NEW policy.
|
|
||||||
/// This will include any new kernel objects which do not require a parent object for creation.
|
|
||||||
NewAny = 3,
|
|
||||||
/// A process under this job is attempting to create a new vm object.
|
|
||||||
NewVMO = 4,
|
|
||||||
/// A process under this job is attempting to create a new channel.
|
|
||||||
NewChannel = 5,
|
|
||||||
/// A process under this job is attempting to create a new event.
|
|
||||||
NewEvent = 6,
|
|
||||||
/// A process under this job is attempting to create a new event pair.
|
|
||||||
NewEventPair = 7,
|
|
||||||
/// A process under this job is attempting to create a new port.
|
|
||||||
NewPort = 8,
|
|
||||||
/// A process under this job is attempting to create a new socket.
|
|
||||||
NewSocket = 9,
|
|
||||||
/// A process under this job is attempting to create a new fifo.
|
|
||||||
NewFIFO = 10,
|
|
||||||
/// A process under this job is attempting to create a new timer.
|
|
||||||
NewTimer = 11,
|
|
||||||
/// A process under this job is attempting to create a new process.
|
|
||||||
NewProcess = 12,
|
|
||||||
/// A process under this job is attempting to create a new profile.
|
|
||||||
NewProfile = 13,
|
|
||||||
/// A process under this job is attempting to use zx_vmo_replace_as_executable()
|
|
||||||
/// with a ZX_HANDLE_INVALID as the second argument rather than a valid ZX_RSRC_KIND_VMEX.
|
|
||||||
AmbientMarkVMOExec = 14,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The action taken when the condition happens specified by a policy.
|
|
||||||
#[repr(u32)]
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
|
||||||
pub enum PolicyAction {
|
|
||||||
/// Allow condition.
|
|
||||||
Allow = 0,
|
|
||||||
/// Prevent condition.
|
|
||||||
Deny = 1,
|
|
||||||
/// Generate an exception via the debug port. An exception generated this
|
|
||||||
/// way acts as a breakpoint. The thread may be resumed after the exception.
|
|
||||||
AllowException = 2,
|
|
||||||
/// Just like `AllowException`, but after resuming condition is denied.
|
|
||||||
DenyException = 3,
|
|
||||||
/// Terminate the process.
|
|
||||||
Kill = 4,
|
|
||||||
}
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
use super::*;
|
|
||||||
|
|
||||||
mod job;
|
|
||||||
mod job_policy;
|
|
||||||
mod process;
|
|
||||||
mod thread;
|
|
||||||
|
|
||||||
pub use {self::job::*, self::job_policy::*, self::process::*, self::thread::*};
|
|
||||||
|
|
||||||
/// Task (Thread, Process, or Job)
|
|
||||||
pub trait Task: Sync + Send {
|
|
||||||
/// Kill the task. The task do not terminate immediately when killed.
|
|
||||||
/// It will terminate after all its children are terminated or some cleanups are finished.
|
|
||||||
fn kill(&self);
|
|
||||||
|
|
||||||
/// Suspend the task. Currently only thread or process handles may be suspended.
|
|
||||||
fn suspend(&self);
|
|
||||||
|
|
||||||
/// Resume the task
|
|
||||||
fn resume(&self);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The return code set when a task is killed via zx_task_kill().
|
|
||||||
pub const TASK_RETCODE_SYSCALL_KILL: i64 = -1028;
|
|
||||||
@ -1,463 +0,0 @@
|
|||||||
use {
|
|
||||||
super::{job::Job, job_policy::*, thread::*, *},
|
|
||||||
crate::{error::*, object::*, vm::*},
|
|
||||||
alloc::{sync::Arc, vec::Vec},
|
|
||||||
hashbrown::HashMap,
|
|
||||||
spin::Mutex,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct Process {
|
|
||||||
base: KObjectBase,
|
|
||||||
job: Arc<Job>,
|
|
||||||
policy: JobPolicy,
|
|
||||||
vmar: Arc<VmAddressRegion>,
|
|
||||||
inner: Mutex<ProcessInner>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_kobject!(Process
|
|
||||||
fn get_child(&self, id: KoID) -> ZxResult<Arc<dyn KernelObject>> {
|
|
||||||
let inner = self.inner.lock();
|
|
||||||
let thread = inner.threads.iter().find(|o| o.id() == id).ok_or(ZxError::NOT_FOUND)?;
|
|
||||||
Ok(thread.clone())
|
|
||||||
}
|
|
||||||
fn related_koid(&self) -> KoID {
|
|
||||||
self.job.id()
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct ProcessInner {
|
|
||||||
max_handle_id: u32,
|
|
||||||
status: Status,
|
|
||||||
handles: HashMap<HandleValue, Handle>,
|
|
||||||
threads: Vec<Arc<Thread>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Status of a process.
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
|
||||||
pub enum Status {
|
|
||||||
/// Initial state, no thread present in process.
|
|
||||||
Init,
|
|
||||||
/// First thread has started and is running.
|
|
||||||
Running,
|
|
||||||
/// Process has exited with the code.
|
|
||||||
Exited(i64),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Status {
|
|
||||||
fn default() -> Self {
|
|
||||||
Status::Init
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Process {
|
|
||||||
/// Create a new process in the `job`.
|
|
||||||
pub fn create(job: &Arc<Job>, name: &str) -> ZxResult<Arc<Self>> {
|
|
||||||
let proc = Arc::new(Process {
|
|
||||||
base: KObjectBase::with_name(name),
|
|
||||||
job: job.clone(),
|
|
||||||
policy: job.policy(),
|
|
||||||
vmar: VmAddressRegion::new_root(),
|
|
||||||
inner: Mutex::new(ProcessInner::default()),
|
|
||||||
});
|
|
||||||
job.add_process(proc.clone())?;
|
|
||||||
Ok(proc)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a handle from the process
|
|
||||||
fn get_handle(&self, handle_value: HandleValue) -> ZxResult<Handle> {
|
|
||||||
self.inner.lock().get_handle(handle_value)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 添加一个新的对象句柄
|
|
||||||
pub fn add_handle(&self, handle: Handle) -> HandleValue {
|
|
||||||
self.inner.lock().add_handle(handle)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 删除一个对象句柄
|
|
||||||
pub fn remove_handle(&self, handle_value: HandleValue) -> ZxResult<Handle> {
|
|
||||||
self.inner.lock().remove_handle(handle_value)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add all handles to the process
|
|
||||||
pub fn add_handles(&self, handles: Vec<Handle>) -> Vec<HandleValue> {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
handles.into_iter().map(|h| inner.add_handle(h)).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove all handles from the process.
|
|
||||||
pub fn remove_handles(&self, handle_values: &[HandleValue]) -> ZxResult<Vec<Handle>> {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
handle_values
|
|
||||||
.iter()
|
|
||||||
.map(|h| inner.remove_handle(*h))
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the kernel object corresponding to this `handle_value`
|
|
||||||
pub fn get_object<T: KernelObject>(&self, handle_value: HandleValue) -> ZxResult<Arc<T>> {
|
|
||||||
let handle = self.get_handle(handle_value)?;
|
|
||||||
let object = handle
|
|
||||||
.object
|
|
||||||
.downcast_arc::<T>()
|
|
||||||
.map_err(|_| ZxError::WRONG_TYPE)?;
|
|
||||||
Ok(object)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 根据句柄值查找内核对象,并检查权限
|
|
||||||
pub fn get_object_with_rights<T: KernelObject>(
|
|
||||||
&self,
|
|
||||||
handle_value: HandleValue,
|
|
||||||
desired_rights: Rights,
|
|
||||||
) -> ZxResult<Arc<T>> {
|
|
||||||
let handle = self.get_handle(handle_value)?;
|
|
||||||
// check type before rights
|
|
||||||
let object = handle
|
|
||||||
.object
|
|
||||||
.downcast_arc::<T>()
|
|
||||||
.map_err(|_| ZxError::WRONG_TYPE)?;
|
|
||||||
if !handle.rights.contains(desired_rights) {
|
|
||||||
return Err(ZxError::ACCESS_DENIED);
|
|
||||||
}
|
|
||||||
Ok(object)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the kernel object corresponding to this `handle_value` and this handle's rights.
|
|
||||||
pub fn get_object_and_rights<T: KernelObject>(
|
|
||||||
&self,
|
|
||||||
handle_value: HandleValue,
|
|
||||||
) -> ZxResult<(Arc<T>, Rights)> {
|
|
||||||
let handle = self.get_handle(handle_value)?;
|
|
||||||
let object = handle
|
|
||||||
.object
|
|
||||||
.downcast_arc::<T>()
|
|
||||||
.map_err(|_| ZxError::WRONG_TYPE)?;
|
|
||||||
Ok((object, handle.rights))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove a handle referring to a kernel object of the given type from the process.
|
|
||||||
pub fn remove_object<T: KernelObject>(&self, handle_value: HandleValue) -> ZxResult<Arc<T>> {
|
|
||||||
let handle = self.remove_handle(handle_value)?;
|
|
||||||
let object = handle
|
|
||||||
.object
|
|
||||||
.downcast_arc::<T>()
|
|
||||||
.map_err(|_| ZxError::WRONG_TYPE)?;
|
|
||||||
Ok(object)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn start(&self) {
|
|
||||||
// unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Exit current process with `retcode`.
|
|
||||||
/// The process do not terminate immediately when exited.
|
|
||||||
/// It will terminate after all its child threads are terminated.
|
|
||||||
pub fn exit(&self, retcode: i64) {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
if let Status::Exited(_) = inner.status {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
inner.status = Status::Exited(retcode);
|
|
||||||
inner.handles.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The process finally terminates.
|
|
||||||
fn terminate(&self) {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
let _retcode = match inner.status {
|
|
||||||
Status::Exited(retcode) => retcode,
|
|
||||||
_ => {
|
|
||||||
inner.status = Status::Exited(0);
|
|
||||||
0
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.job.remove_process(self.base.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check whether `condition` is allowed in the parent job's policy.
|
|
||||||
pub fn check_policy(&self, condition: PolicyCondition) -> ZxResult {
|
|
||||||
match self
|
|
||||||
.policy
|
|
||||||
.get_action(condition)
|
|
||||||
.unwrap_or(PolicyAction::Allow)
|
|
||||||
{
|
|
||||||
PolicyAction::Allow => Ok(()),
|
|
||||||
PolicyAction::Deny => Err(ZxError::ACCESS_DENIED),
|
|
||||||
_ => unimplemented!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get process status.
|
|
||||||
pub fn status(&self) -> Status {
|
|
||||||
self.inner.lock().status
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the `VmAddressRegion` of the process.
|
|
||||||
pub fn vmar(&self) -> Arc<VmAddressRegion> {
|
|
||||||
self.vmar.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the job of the process.
|
|
||||||
pub fn job(&self) -> Arc<Job> {
|
|
||||||
self.job.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a thread to the process.
|
|
||||||
pub(super) fn add_thread(&self, thread: Arc<Thread>) -> ZxResult {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
if let Status::Exited(_) = inner.status {
|
|
||||||
return Err(ZxError::BAD_STATE);
|
|
||||||
}
|
|
||||||
inner.threads.push(thread);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove a thread from the process.
|
|
||||||
///
|
|
||||||
/// If no more threads left, exit the process.
|
|
||||||
pub(super) fn remove_thread(&self, tid: KoID) {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
inner.threads.retain(|t| t.id() != tid);
|
|
||||||
if inner.threads.is_empty() {
|
|
||||||
drop(inner);
|
|
||||||
self.terminate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get KoIDs of Threads.
|
|
||||||
pub fn thread_ids(&self) -> Vec<KoID> {
|
|
||||||
self.inner.lock().threads.iter().map(|t| t.id()).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get information of this process.
|
|
||||||
pub fn get_info(&self) -> ProcessInfo {
|
|
||||||
let mut info = ProcessInfo {
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
match self.inner.lock().status {
|
|
||||||
Status::Init => {
|
|
||||||
info.started = false;
|
|
||||||
info.has_exited = false;
|
|
||||||
}
|
|
||||||
Status::Running => {
|
|
||||||
info.started = true;
|
|
||||||
info.has_exited = false;
|
|
||||||
}
|
|
||||||
Status::Exited(ret) => {
|
|
||||||
info.return_code = ret;
|
|
||||||
info.has_exited = true;
|
|
||||||
info.started = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
info
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Information of a process.
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct ProcessInfo {
|
|
||||||
pub return_code: i64,
|
|
||||||
pub started: bool,
|
|
||||||
pub has_exited: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Task for Process {
|
|
||||||
fn kill(&self) {
|
|
||||||
self.exit(TASK_RETCODE_SYSCALL_KILL);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn suspend(&self) {
|
|
||||||
// let inner = self.inner.lock();
|
|
||||||
// for thread in inner.threads.iter() {
|
|
||||||
// thread.suspend();
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resume(&self) {
|
|
||||||
// let inner = self.inner.lock();
|
|
||||||
// for thread in inner.threads.iter() {
|
|
||||||
// thread.resume();
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ProcessInner {
|
|
||||||
/// Add a handle to the process
|
|
||||||
fn add_handle(&mut self, handle: Handle) -> HandleValue {
|
|
||||||
let key = (self.max_handle_id << 2) | 0x3u32;
|
|
||||||
self.max_handle_id += 1;
|
|
||||||
self.handles.insert(key, handle);
|
|
||||||
key
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_handle(&mut self, handle_value: HandleValue) -> ZxResult<Handle> {
|
|
||||||
let handle = self
|
|
||||||
.handles
|
|
||||||
.remove(&handle_value)
|
|
||||||
.ok_or(ZxError::BAD_HANDLE)?;
|
|
||||||
Ok(handle)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_handle(&mut self, handle_value: HandleValue) -> ZxResult<Handle> {
|
|
||||||
let handle = self.handles.get(&handle_value).ok_or(ZxError::BAD_HANDLE)?;
|
|
||||||
Ok(handle.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether `thread` is in this process.
|
|
||||||
fn contains_thread(&self, thread: &Arc<Thread>) -> bool {
|
|
||||||
self.threads.iter().any(|t| Arc::ptr_eq(t, thread))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn create() {
|
|
||||||
let root_job = Job::root();
|
|
||||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
|
||||||
|
|
||||||
assert_eq!(proc.related_koid(), root_job.id());
|
|
||||||
assert!(Arc::ptr_eq(&root_job, &proc.job()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn handle() {
|
|
||||||
let root_job = Job::root();
|
|
||||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
|
||||||
let handle = Handle::new(proc.clone(), Rights::DEFAULT_PROCESS);
|
|
||||||
|
|
||||||
let handle_value = proc.add_handle(handle);
|
|
||||||
|
|
||||||
// getting object should success
|
|
||||||
let object: Arc<Process> = proc
|
|
||||||
.get_object_with_rights(handle_value, Rights::DEFAULT_PROCESS)
|
|
||||||
.expect("failed to get object");
|
|
||||||
assert!(Arc::ptr_eq(&object, &proc));
|
|
||||||
|
|
||||||
let (object, rights) = proc
|
|
||||||
.get_object_and_rights::<Process>(handle_value)
|
|
||||||
.expect("failed to get object");
|
|
||||||
assert!(Arc::ptr_eq(&object, &proc));
|
|
||||||
assert_eq!(rights, Rights::DEFAULT_PROCESS);
|
|
||||||
|
|
||||||
// getting object with an extra rights should fail.
|
|
||||||
assert_eq!(
|
|
||||||
proc.get_object_with_rights::<Process>(handle_value, Rights::MANAGE_JOB)
|
|
||||||
.err(),
|
|
||||||
Some(ZxError::ACCESS_DENIED)
|
|
||||||
);
|
|
||||||
|
|
||||||
// getting object with invalid type should fail.
|
|
||||||
assert_eq!(
|
|
||||||
proc.get_object_with_rights::<Job>(handle_value, Rights::DEFAULT_PROCESS)
|
|
||||||
.err(),
|
|
||||||
Some(ZxError::WRONG_TYPE)
|
|
||||||
);
|
|
||||||
|
|
||||||
proc.remove_handle(handle_value).unwrap();
|
|
||||||
|
|
||||||
// getting object with invalid handle should fail.
|
|
||||||
assert_eq!(
|
|
||||||
proc.get_object_with_rights::<Process>(handle_value, Rights::DEFAULT_PROCESS)
|
|
||||||
.err(),
|
|
||||||
Some(ZxError::BAD_HANDLE)
|
|
||||||
);
|
|
||||||
|
|
||||||
let handle1 = Handle::new(proc.clone(), Rights::DEFAULT_PROCESS);
|
|
||||||
let handle2 = Handle::new(proc.clone(), Rights::DEFAULT_PROCESS);
|
|
||||||
|
|
||||||
let handle_values = proc.add_handles(vec![handle1, handle2]);
|
|
||||||
let object1: Arc<Process> = proc
|
|
||||||
.get_object_with_rights(handle_values[0], Rights::DEFAULT_PROCESS)
|
|
||||||
.expect("failed to get object");
|
|
||||||
assert!(Arc::ptr_eq(&object1, &proc));
|
|
||||||
|
|
||||||
proc.remove_handles(&handle_values).unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
proc.get_object_with_rights::<Process>(handle_values[0], Rights::DEFAULT_PROCESS)
|
|
||||||
.err(),
|
|
||||||
Some(ZxError::BAD_HANDLE)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_child() {
|
|
||||||
let root_job = Job::root();
|
|
||||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
|
||||||
let thread = Thread::create(&proc, "thread").expect("failed to create thread");
|
|
||||||
|
|
||||||
assert_eq!(proc.get_child(thread.id()).unwrap().id(), thread.id());
|
|
||||||
assert_eq!(proc.get_child(proc.id()).err(), Some(ZxError::NOT_FOUND));
|
|
||||||
|
|
||||||
let thread1 = Thread::create(&proc, "thread1").expect("failed to create thread");
|
|
||||||
assert_eq!(proc.thread_ids(), vec![thread.id(), thread1.id()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn contains_thread() {
|
|
||||||
let root_job = Job::root();
|
|
||||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
|
||||||
let thread = Thread::create(&proc, "thread").expect("failed to create thread");
|
|
||||||
|
|
||||||
let proc1 = Process::create(&root_job, "proc1").expect("failed to create process");
|
|
||||||
let thread1 = Thread::create(&proc1, "thread1").expect("failed to create thread");
|
|
||||||
|
|
||||||
let inner = proc.inner.lock();
|
|
||||||
assert!(inner.contains_thread(&thread) && !inner.contains_thread(&thread1));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn check_policy() {
|
|
||||||
let root_job = Job::root();
|
|
||||||
let policy1 = BasicPolicy {
|
|
||||||
condition: PolicyCondition::BadHandle,
|
|
||||||
action: PolicyAction::Allow,
|
|
||||||
};
|
|
||||||
let policy2 = BasicPolicy {
|
|
||||||
condition: PolicyCondition::NewChannel,
|
|
||||||
action: PolicyAction::Deny,
|
|
||||||
};
|
|
||||||
|
|
||||||
assert!(root_job
|
|
||||||
.set_policy_basic(SetPolicyOptions::Absolute, &[policy1, policy2])
|
|
||||||
.is_ok());
|
|
||||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
|
||||||
|
|
||||||
assert!(proc.check_policy(PolicyCondition::BadHandle).is_ok());
|
|
||||||
assert!(proc.check_policy(PolicyCondition::NewProcess).is_ok());
|
|
||||||
assert_eq!(
|
|
||||||
proc.check_policy(PolicyCondition::NewChannel).err(),
|
|
||||||
Some(ZxError::ACCESS_DENIED)
|
|
||||||
);
|
|
||||||
|
|
||||||
let _job = root_job.create_child().unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
root_job
|
|
||||||
.set_policy_basic(SetPolicyOptions::Absolute, &[policy1, policy2])
|
|
||||||
.err(),
|
|
||||||
Some(ZxError::BAD_STATE)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn exit() {
|
|
||||||
let root_job = Job::root();
|
|
||||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
|
||||||
|
|
||||||
let info = proc.get_info();
|
|
||||||
assert!(!info.has_exited && !info.started && info.return_code == 0);
|
|
||||||
|
|
||||||
proc.exit(666);
|
|
||||||
let info = proc.get_info();
|
|
||||||
assert!(info.has_exited && info.started && info.return_code == 666);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
Thread::create(&proc, "thread1").err(),
|
|
||||||
Some(ZxError::BAD_STATE)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
use {super::process::Process, super::*, crate::object::*, alloc::sync::Arc};
|
|
||||||
|
|
||||||
pub struct Thread {
|
|
||||||
base: KObjectBase,
|
|
||||||
proc: Arc<Process>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_kobject!(Thread
|
|
||||||
fn related_koid(&self) -> KoID {
|
|
||||||
self.proc.id()
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
impl Thread {
|
|
||||||
/// Create a new thread.
|
|
||||||
pub fn create(proc: &Arc<Process>, name: &str) -> ZxResult<Arc<Self>> {
|
|
||||||
let thread = Arc::new(Thread {
|
|
||||||
base: KObjectBase::with_name(name),
|
|
||||||
proc: proc.clone(),
|
|
||||||
});
|
|
||||||
proc.add_thread(thread.clone())?;
|
|
||||||
Ok(thread)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
//! Objects for Virtual Memory Management.
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
mod vmar;
|
|
||||||
|
|
||||||
pub use self::vmar::*;
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
use {super::*, crate::object::*, alloc::sync::Arc};
|
|
||||||
|
|
||||||
/// Virtual Memory Address Regions
|
|
||||||
pub struct VmAddressRegion {
|
|
||||||
base: KObjectBase,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_kobject!(VmAddressRegion);
|
|
||||||
|
|
||||||
impl VmAddressRegion {
|
|
||||||
/// Create a new root VMAR.
|
|
||||||
pub fn new_root() -> Arc<Self> {
|
|
||||||
Arc::new(VmAddressRegion {
|
|
||||||
base: KObjectBase::new(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
[workspace]
|
|
||||||
members = [
|
|
||||||
"object",
|
|
||||||
"kernel-hal-unix",
|
|
||||||
"kernel-hal",
|
|
||||||
]
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "kernel-hal-unix"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Runji Wang <wangrunji0408@163.com>"]
|
|
||||||
edition = "2018"
|
|
||||||
description = "Kernel HAL implementation on Linux and macOS."
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
kernel-hal = { path = "../kernel-hal" }
|
|
||||||
async-std = "1.9"
|
|
||||||
trapframe = "0.8.0"
|
|
||||||
@ -1,46 +0,0 @@
|
|||||||
#![feature(asm)]
|
|
||||||
#![feature(linkage)]
|
|
||||||
#![deny(warnings)]
|
|
||||||
|
|
||||||
extern crate alloc;
|
|
||||||
|
|
||||||
use {
|
|
||||||
alloc::boxed::Box,
|
|
||||||
core::time::Duration,
|
|
||||||
core::{future::Future, pin::Pin},
|
|
||||||
std::time::SystemTime,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use trapframe::{GeneralRegs, UserContext};
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Thread {
|
|
||||||
thread: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Thread {
|
|
||||||
#[export_name = "hal_thread_spawn"]
|
|
||||||
pub fn spawn(
|
|
||||||
future: Pin<Box<dyn Future<Output = ()> + Send + 'static>>,
|
|
||||||
_vmtoken: usize,
|
|
||||||
) -> Self {
|
|
||||||
async_std::task::spawn(future);
|
|
||||||
Thread { thread: 0 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get current time.
|
|
||||||
#[export_name = "hal_timer_now"]
|
|
||||||
pub fn timer_now() -> Duration {
|
|
||||||
SystemTime::now()
|
|
||||||
.duration_since(SystemTime::UNIX_EPOCH)
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Initialize the HAL.
|
|
||||||
///
|
|
||||||
/// This function must be called at the beginning.
|
|
||||||
pub fn init() {
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "kernel-hal"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Runji Wang <wangrunji0408@163.com>"]
|
|
||||||
edition = "2018"
|
|
||||||
description = "Kernel HAL interface definations."
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
trapframe = "0.8.0"
|
|
||||||
@ -1,35 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
#![feature(linkage)]
|
|
||||||
#![deny(warnings)]
|
|
||||||
|
|
||||||
extern crate alloc;
|
|
||||||
|
|
||||||
pub use trapframe::{GeneralRegs, UserContext};
|
|
||||||
|
|
||||||
use {
|
|
||||||
alloc::boxed::Box,
|
|
||||||
core::{future::Future, pin::Pin, time::Duration},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Thread {
|
|
||||||
id: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Thread {
|
|
||||||
/// Spawn a new thread.
|
|
||||||
#[linkage = "weak"]
|
|
||||||
#[export_name = "hal_thread_spawn"]
|
|
||||||
pub fn spawn(
|
|
||||||
_future: Pin<Box<dyn Future<Output = ()> + Send + 'static>>,
|
|
||||||
_vmtoken: usize,
|
|
||||||
) -> Self {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[linkage = "weak"]
|
|
||||||
#[export_name = "hal_timer_now"]
|
|
||||||
pub fn timer_now() -> Duration {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "object"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Runji Wang <wangrunji0408@163.com>"]
|
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
spin = "0.7"
|
|
||||||
downcast-rs = { version = "1.2.0", default-features = false }
|
|
||||||
bitflags = "1.2"
|
|
||||||
hashbrown = "0.9"
|
|
||||||
trapframe = "0.8.0"
|
|
||||||
futures = { version = "0.3", default-features = false, features = ["alloc", "async-await"] }
|
|
||||||
async-std = { version = "1.9", features = ["attributes", "unstable"] }
|
|
||||||
numeric-enum-macro = "0.2"
|
|
||||||
kernel-hal = { path = "../kernel-hal" }
|
|
||||||
kernel-hal-unix = { path = "../kernel-hal-unix" }
|
|
||||||
@ -1,232 +0,0 @@
|
|||||||
// ANCHOR: result
|
|
||||||
///
|
|
||||||
pub type ZxResult<T = ()> = Result<T, ZxError>;
|
|
||||||
// ANCHOR_END: result
|
|
||||||
|
|
||||||
// ANCHOR: error_begin
|
|
||||||
/// Zircon statuses are signed 32 bit integers. The space of values is
|
|
||||||
/// divided as follows:
|
|
||||||
/// - The zero value is for the OK status.
|
|
||||||
/// - Negative values are defined by the system, in this file.
|
|
||||||
/// - Positive values are reserved for protocol-specific error values,
|
|
||||||
/// and will never be defined by the system.
|
|
||||||
#[allow(non_camel_case_types, dead_code)]
|
|
||||||
#[repr(i32)]
|
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
|
||||||
pub enum ZxError {
|
|
||||||
OK = 0,
|
|
||||||
|
|
||||||
// ======= Internal failures =======
|
|
||||||
/// The system encountered an otherwise unspecified error
|
|
||||||
/// while performing the operation.
|
|
||||||
INTERNAL = -1,
|
|
||||||
|
|
||||||
/// The operation is not implemented, supported,
|
|
||||||
/// or enabled.
|
|
||||||
NOT_SUPPORTED = -2,
|
|
||||||
// ANCHOR_END: error_begin
|
|
||||||
/// The system was not able to allocate some resource
|
|
||||||
/// needed for the operation.
|
|
||||||
NO_RESOURCES = -3,
|
|
||||||
|
|
||||||
/// The system was not able to allocate memory needed
|
|
||||||
/// for the operation.
|
|
||||||
NO_MEMORY = -4,
|
|
||||||
|
|
||||||
// -5 used to be ZX_ERR_CALL_FAILED.
|
|
||||||
/// The system call was interrupted, but should be
|
|
||||||
/// retried. This should not be seen outside of the VDSO.
|
|
||||||
INTERNAL_INTR_RETRY = -6,
|
|
||||||
|
|
||||||
// ======= Parameter errors =======
|
|
||||||
/// an argument is invalid, ex. null pointer
|
|
||||||
INVALID_ARGS = -10,
|
|
||||||
|
|
||||||
/// A specified handle value does not refer to a handle.
|
|
||||||
BAD_HANDLE = -11,
|
|
||||||
|
|
||||||
/// The subject of the operation is the wrong type to
|
|
||||||
/// perform the operation.
|
|
||||||
/// Example: Attempting a message_read on a thread handle.
|
|
||||||
WRONG_TYPE = -12,
|
|
||||||
|
|
||||||
/// The specified syscall number is invalid.
|
|
||||||
BAD_SYSCALL = -13,
|
|
||||||
|
|
||||||
/// An argument is outside the valid range for this
|
|
||||||
/// operation.
|
|
||||||
OUT_OF_RANGE = -14,
|
|
||||||
|
|
||||||
/// A caller provided buffer is too small for
|
|
||||||
/// this operation.
|
|
||||||
BUFFER_TOO_SMALL = -15,
|
|
||||||
|
|
||||||
// ======= Precondition or state errors =======
|
|
||||||
/// operation failed because the current state of the
|
|
||||||
/// object does not allow it, or a precondition of the operation is
|
|
||||||
/// not satisfied
|
|
||||||
BAD_STATE = -20,
|
|
||||||
|
|
||||||
/// The time limit for the operation elapsed before
|
|
||||||
/// the operation completed.
|
|
||||||
TIMED_OUT = -21,
|
|
||||||
|
|
||||||
/// The operation cannot be performed currently but
|
|
||||||
/// potentially could succeed if the caller waits for a prerequisite
|
|
||||||
/// to be satisfied, for example waiting for a handle to be readable
|
|
||||||
/// or writable.
|
|
||||||
/// Example: Attempting to read from a channel that has no
|
|
||||||
/// messages waiting but has an open remote will return ZX_ERR_SHOULD_WAIT.
|
|
||||||
/// Attempting to read from a channel that has no messages waiting
|
|
||||||
/// and has a closed remote end will return ZX_ERR_PEER_CLOSED.
|
|
||||||
SHOULD_WAIT = -22,
|
|
||||||
|
|
||||||
/// The in-progress operation (e.g. a wait) has been
|
|
||||||
/// canceled.
|
|
||||||
CANCELED = -23,
|
|
||||||
|
|
||||||
/// The operation failed because the remote end of the
|
|
||||||
/// subject of the operation was closed.
|
|
||||||
PEER_CLOSED = -24,
|
|
||||||
|
|
||||||
/// The requested entity is not found.
|
|
||||||
NOT_FOUND = -25,
|
|
||||||
|
|
||||||
/// An object with the specified identifier
|
|
||||||
/// already exists.
|
|
||||||
/// Example: Attempting to create a file when a file already exists
|
|
||||||
/// with that name.
|
|
||||||
ALREADY_EXISTS = -26,
|
|
||||||
|
|
||||||
/// The operation failed because the named entity
|
|
||||||
/// is already owned or controlled by another entity. The operation
|
|
||||||
/// could succeed later if the current owner releases the entity.
|
|
||||||
ALREADY_BOUND = -27,
|
|
||||||
|
|
||||||
/// The subject of the operation is currently unable
|
|
||||||
/// to perform the operation.
|
|
||||||
/// Note: This is used when there's no direct way for the caller to
|
|
||||||
/// observe when the subject will be able to perform the operation
|
|
||||||
/// and should thus retry.
|
|
||||||
UNAVAILABLE = -28,
|
|
||||||
|
|
||||||
// ======= Permission check errors =======
|
|
||||||
/// The caller did not have permission to perform
|
|
||||||
/// the specified operation.
|
|
||||||
ACCESS_DENIED = -30,
|
|
||||||
|
|
||||||
// ======= Input-output errors =======
|
|
||||||
/// Otherwise unspecified error occurred during I/O.
|
|
||||||
IO = -40,
|
|
||||||
|
|
||||||
/// The entity the I/O operation is being performed on
|
|
||||||
/// rejected the operation.
|
|
||||||
/// Example: an I2C device NAK'ing a transaction or a disk controller
|
|
||||||
/// rejecting an invalid command, or a stalled USB endpoint.
|
|
||||||
IO_REFUSED = -41,
|
|
||||||
|
|
||||||
/// The data in the operation failed an integrity
|
|
||||||
/// check and is possibly corrupted.
|
|
||||||
/// Example: CRC or Parity error.
|
|
||||||
IO_DATA_INTEGRITY = -42,
|
|
||||||
|
|
||||||
/// The data in the operation is currently unavailable
|
|
||||||
/// and may be permanently lost.
|
|
||||||
/// Example: A disk block is irrecoverably damaged.
|
|
||||||
IO_DATA_LOSS = -43,
|
|
||||||
|
|
||||||
/// The device is no longer available (has been
|
|
||||||
/// unplugged from the system, powered down, or the driver has been
|
|
||||||
/// unloaded,
|
|
||||||
IO_NOT_PRESENT = -44,
|
|
||||||
|
|
||||||
/// More data was received from the device than expected.
|
|
||||||
/// Example: a USB "babble" error due to a device sending more data than
|
|
||||||
/// the host queued to receive.
|
|
||||||
IO_OVERRUN = -45,
|
|
||||||
|
|
||||||
/// An operation did not complete within the required timeframe.
|
|
||||||
/// Example: A USB isochronous transfer that failed to complete due to an overrun or underrun.
|
|
||||||
IO_MISSED_DEADLINE = -46,
|
|
||||||
|
|
||||||
/// The data in the operation is invalid parameter or is out of range.
|
|
||||||
/// Example: A USB transfer that failed to complete with TRB Error
|
|
||||||
IO_INVALID = -47,
|
|
||||||
|
|
||||||
// ======== Filesystem Errors ========
|
|
||||||
/// Path name is too long.
|
|
||||||
BAD_PATH = -50,
|
|
||||||
|
|
||||||
/// Object is not a directory or does not support
|
|
||||||
/// directory operations.
|
|
||||||
/// Example: Attempted to open a file as a directory or
|
|
||||||
/// attempted to do directory operations on a file.
|
|
||||||
NOT_DIR = -51,
|
|
||||||
|
|
||||||
/// Object is not a regular file.
|
|
||||||
NOT_FILE = -52,
|
|
||||||
|
|
||||||
/// This operation would cause a file to exceed a
|
|
||||||
/// filesystem-specific size limit
|
|
||||||
FILE_BIG = -53,
|
|
||||||
|
|
||||||
/// Filesystem or device space is exhausted.
|
|
||||||
NO_SPACE = -54,
|
|
||||||
|
|
||||||
/// Directory is not empty.
|
|
||||||
NOT_EMPTY = -55,
|
|
||||||
|
|
||||||
// ======== Flow Control ========
|
|
||||||
// These are not errors, as such, and will never be returned
|
|
||||||
// by a syscall or public API. They exist to allow callbacks
|
|
||||||
// to request changes in operation.
|
|
||||||
/// Do not call again.
|
|
||||||
/// Example: A notification callback will be called on every
|
|
||||||
/// event until it returns something other than ZX_OK.
|
|
||||||
/// This status allows differentiation between "stop due to
|
|
||||||
/// an error" and "stop because the work is done."
|
|
||||||
STOP = -60,
|
|
||||||
|
|
||||||
/// Advance to the next item.
|
|
||||||
/// Example: A notification callback will use this response
|
|
||||||
/// to indicate it did not "consume" an item passed to it,
|
|
||||||
/// but by choice, not due to an error condition.
|
|
||||||
NEXT = -61,
|
|
||||||
|
|
||||||
/// Ownership of the item has moved to an asynchronous worker.
|
|
||||||
///
|
|
||||||
/// Unlike ZX_ERR_STOP, which implies that iteration on an object
|
|
||||||
/// should stop, and ZX_ERR_NEXT, which implies that iteration
|
|
||||||
/// should continue to the next item, ZX_ERR_ASYNC implies
|
|
||||||
/// that an asynchronous worker is responsible for continuing iteration.
|
|
||||||
///
|
|
||||||
/// Example: A notification callback will be called on every
|
|
||||||
/// event, but one event needs to handle some work asynchronously
|
|
||||||
/// before it can continue. ZX_ERR_ASYNC implies the worker is
|
|
||||||
/// responsible for resuming iteration once its work has completed.
|
|
||||||
ASYNC = -62,
|
|
||||||
|
|
||||||
// ======== Network-related errors ========
|
|
||||||
/// Specified protocol is not
|
|
||||||
/// supported.
|
|
||||||
PROTOCOL_NOT_SUPPORTED = -70,
|
|
||||||
|
|
||||||
/// Host is unreachable.
|
|
||||||
ADDRESS_UNREACHABLE = -71,
|
|
||||||
|
|
||||||
/// Address is being used by someone else.
|
|
||||||
ADDRESS_IN_USE = -72,
|
|
||||||
|
|
||||||
/// Socket is not connected.
|
|
||||||
NOT_CONNECTED = -73,
|
|
||||||
|
|
||||||
/// Remote peer rejected the connection.
|
|
||||||
CONNECTION_REFUSED = -74,
|
|
||||||
|
|
||||||
/// Connection was reset.
|
|
||||||
CONNECTION_RESET = -75,
|
|
||||||
// ANCHOR: error_end
|
|
||||||
/// Connection was aborted.
|
|
||||||
CONNECTION_ABORTED = -76,
|
|
||||||
}
|
|
||||||
// ANCHOR_END: error_end
|
|
||||||
@ -1,176 +0,0 @@
|
|||||||
use {
|
|
||||||
super::*,
|
|
||||||
crate::error::*,
|
|
||||||
crate::object::*,
|
|
||||||
alloc::collections::VecDeque,
|
|
||||||
alloc::sync::{Arc, Weak},
|
|
||||||
alloc::vec::Vec,
|
|
||||||
core::sync::atomic::{AtomicU32, Ordering},
|
|
||||||
spin::Mutex,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct Channel {
|
|
||||||
base: KObjectBase,
|
|
||||||
peer: Weak<Channel>,
|
|
||||||
recv_queue: Mutex<VecDeque<T>>,
|
|
||||||
next_txid: AtomicU32,
|
|
||||||
}
|
|
||||||
|
|
||||||
type T = MessagePacket;
|
|
||||||
type TxID = u32;
|
|
||||||
|
|
||||||
impl_kobject!(Channel
|
|
||||||
fn peer(&self) -> ZxResult<Arc<dyn KernelObject>> {
|
|
||||||
let peer = self.peer.upgrade().ok_or(ZxError::PEER_CLOSED)?;
|
|
||||||
Ok(peer)
|
|
||||||
}
|
|
||||||
fn related_koid(&self) -> KoID {
|
|
||||||
self.peer.upgrade().map(|p| p.id()).unwrap_or(0)
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
impl Channel {
|
|
||||||
/// Create a channel and return a pair of its endpoints
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
pub fn create() -> (Arc<Self>, Arc<Self>) {
|
|
||||||
let mut channel0 = Arc::new(Channel {
|
|
||||||
base: KObjectBase::default(),
|
|
||||||
peer: Weak::default(),
|
|
||||||
recv_queue: Default::default(),
|
|
||||||
next_txid: AtomicU32::new(0x8000_0000),
|
|
||||||
});
|
|
||||||
let channel1 = Arc::new(Channel {
|
|
||||||
base: KObjectBase::default(),
|
|
||||||
peer: Arc::downgrade(&channel0),
|
|
||||||
recv_queue: Default::default(),
|
|
||||||
next_txid: AtomicU32::new(0x8000_0000),
|
|
||||||
});
|
|
||||||
// no other reference of `channel0`
|
|
||||||
unsafe {
|
|
||||||
Arc::get_mut_unchecked(&mut channel0).peer = Arc::downgrade(&channel1);
|
|
||||||
}
|
|
||||||
(channel0, channel1)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read a packet from the channel if check is ok, otherwise the msg will keep.
|
|
||||||
pub fn read(&self) -> ZxResult<T> {
|
|
||||||
let mut recv_queue = self.recv_queue.lock();
|
|
||||||
if recv_queue.front().is_some() {
|
|
||||||
let msg = recv_queue.pop_front().unwrap();
|
|
||||||
return Ok(msg);
|
|
||||||
}
|
|
||||||
if self.peer_closed() {
|
|
||||||
Err(ZxError::PEER_CLOSED)
|
|
||||||
} else {
|
|
||||||
Err(ZxError::SHOULD_WAIT)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write a packet to the channel
|
|
||||||
pub fn write(&self, msg: T) -> ZxResult {
|
|
||||||
let peer = self.peer.upgrade().ok_or(ZxError::PEER_CLOSED)?;
|
|
||||||
peer.push_general(msg);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Push a message to general queue, called from peer.
|
|
||||||
fn push_general(&self, msg: T) {
|
|
||||||
let mut send_queue = self.recv_queue.lock();
|
|
||||||
send_queue.push_back(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generate a new transaction ID for `call`.
|
|
||||||
fn new_txid(&self) -> TxID {
|
|
||||||
self.next_txid.fetch_add(1, Ordering::SeqCst)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Is peer channel closed?
|
|
||||||
fn peer_closed(&self) -> bool {
|
|
||||||
self.peer.strong_count() == 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The message transferred in the channel.
|
|
||||||
/// See [Channel](struct.Channel.html) for details.
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct MessagePacket {
|
|
||||||
/// The transition id of the message packet
|
|
||||||
pub txid: TxID,
|
|
||||||
/// The data carried by the message packet
|
|
||||||
pub data: Vec<u8>,
|
|
||||||
/// See [Channel](struct.Channel.html) for details.
|
|
||||||
pub handles: Vec<Handle>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_basics() {
|
|
||||||
let (end0, end1) = Channel::create();
|
|
||||||
assert!(Arc::ptr_eq(
|
|
||||||
&end0.peer().unwrap().downcast_arc().unwrap(),
|
|
||||||
&end1
|
|
||||||
));
|
|
||||||
assert_eq!(end0.related_koid(), end1.id());
|
|
||||||
|
|
||||||
drop(end1);
|
|
||||||
assert_eq!(end0.peer().unwrap_err(), ZxError::PEER_CLOSED);
|
|
||||||
assert_eq!(end0.related_koid(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_write() {
|
|
||||||
let (channel0, channel1) = Channel::create();
|
|
||||||
// write a message to each other
|
|
||||||
let txid0 = channel0.new_txid();
|
|
||||||
channel0
|
|
||||||
.write(MessagePacket {
|
|
||||||
txid: txid0,
|
|
||||||
data: Vec::from("hello 1"),
|
|
||||||
handles: Vec::new(),
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
let txid1 = channel1.new_txid();
|
|
||||||
channel1
|
|
||||||
.write(MessagePacket {
|
|
||||||
txid: txid1,
|
|
||||||
data: Vec::from("hello 0"),
|
|
||||||
handles: Vec::new(),
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// read message should success
|
|
||||||
let recv_msg = channel1.read().unwrap();
|
|
||||||
assert_eq!(recv_msg.txid, txid0);
|
|
||||||
assert_eq!(recv_msg.data.as_slice(), b"hello 1");
|
|
||||||
assert!(recv_msg.handles.is_empty());
|
|
||||||
|
|
||||||
let recv_msg = channel0.read().unwrap();
|
|
||||||
assert_eq!(recv_msg.txid, txid1);
|
|
||||||
assert_eq!(recv_msg.data.as_slice(), b"hello 0");
|
|
||||||
assert!(recv_msg.handles.is_empty());
|
|
||||||
|
|
||||||
// read more message should fail.
|
|
||||||
assert_eq!(channel0.read().err(), Some(ZxError::SHOULD_WAIT));
|
|
||||||
assert_eq!(channel1.read().err(), Some(ZxError::SHOULD_WAIT));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn peer_closed() {
|
|
||||||
let (channel0, channel1) = Channel::create();
|
|
||||||
// write a message from peer, then drop it
|
|
||||||
channel1.write(MessagePacket::default()).unwrap();
|
|
||||||
drop(channel1);
|
|
||||||
// read the first message should success.
|
|
||||||
channel0.read().unwrap();
|
|
||||||
// read more message should fail.
|
|
||||||
assert_eq!(channel0.read().err(), Some(ZxError::PEER_CLOSED));
|
|
||||||
// write message should fail.
|
|
||||||
assert_eq!(
|
|
||||||
channel0.write(MessagePacket::default()),
|
|
||||||
Err(ZxError::PEER_CLOSED)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
use super::*;
|
|
||||||
|
|
||||||
mod channel;
|
|
||||||
pub use self::channel::*;
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
#![deny(unused_imports)]
|
|
||||||
#![allow(dead_code)]
|
|
||||||
#![feature(get_mut_unchecked)]
|
|
||||||
|
|
||||||
extern crate alloc;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
#[macro_use]
|
|
||||||
extern crate std;
|
|
||||||
|
|
||||||
mod error;
|
|
||||||
mod ipc;
|
|
||||||
mod object;
|
|
||||||
mod task;
|
|
||||||
mod vm;
|
|
||||||
|
|
||||||
pub use self::error::*;
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
// ANCHOR: handle
|
|
||||||
use super::{KernelObject, Rights};
|
|
||||||
use alloc::sync::Arc;
|
|
||||||
|
|
||||||
pub type HandleValue = u32;
|
|
||||||
pub const INVALID_HANDLE: HandleValue = 0;
|
|
||||||
|
|
||||||
/// 内核对象句柄
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Handle {
|
|
||||||
pub object: Arc<dyn KernelObject>,
|
|
||||||
pub rights: Rights,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Handle {
|
|
||||||
/// 创建一个新句柄
|
|
||||||
pub fn new(object: Arc<dyn KernelObject>, rights: Rights) -> Self {
|
|
||||||
Handle { object, rights }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ANCHOR_END: handle
|
|
||||||
@ -1,409 +0,0 @@
|
|||||||
use {
|
|
||||||
super::job_policy::*,
|
|
||||||
super::process::Process,
|
|
||||||
super::*,
|
|
||||||
crate::error::*,
|
|
||||||
crate::object::*,
|
|
||||||
crate::task::Task,
|
|
||||||
alloc::sync::{Arc, Weak},
|
|
||||||
alloc::vec::Vec,
|
|
||||||
spin::Mutex,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Job 对象
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub struct Job {
|
|
||||||
base: KObjectBase,
|
|
||||||
parent: Option<Arc<Job>>,
|
|
||||||
parent_policy: JobPolicy,
|
|
||||||
inner: Mutex<JobInner>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_kobject!(Job
|
|
||||||
fn get_child(&self, id: KoID) -> ZxResult<Arc<dyn KernelObject>> {
|
|
||||||
let inner = self.inner.lock();
|
|
||||||
if let Some(job) = inner.children.iter().filter_map(|o|o.upgrade()).find(|o| o.id() == id) {
|
|
||||||
return Ok(job);
|
|
||||||
}
|
|
||||||
if let Some(proc) = inner.processes.iter().find(|o| o.id() == id) {
|
|
||||||
return Ok(proc.clone());
|
|
||||||
}
|
|
||||||
Err(ZxError::NOT_FOUND)
|
|
||||||
}
|
|
||||||
fn related_koid(&self) -> KoID {
|
|
||||||
self.parent.as_ref().map(|p| p.id()).unwrap_or(0)
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct JobInner {
|
|
||||||
policy: JobPolicy,
|
|
||||||
children: Vec<Weak<Job>>,
|
|
||||||
processes: Vec<Arc<Process>>,
|
|
||||||
// if the job is killed, no more child creation should works
|
|
||||||
killed: bool,
|
|
||||||
self_ref: Weak<Job>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Job {
|
|
||||||
/// Create the root job.
|
|
||||||
pub fn root() -> Arc<Self> {
|
|
||||||
let job = Arc::new(Job {
|
|
||||||
base: KObjectBase::new(),
|
|
||||||
parent: None,
|
|
||||||
parent_policy: JobPolicy::default(),
|
|
||||||
inner: Mutex::new(JobInner::default()),
|
|
||||||
});
|
|
||||||
job.inner.lock().self_ref = Arc::downgrade(&job);
|
|
||||||
job
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new child job object.
|
|
||||||
pub fn create_child(self: &Arc<Self>) -> ZxResult<Arc<Self>> {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
if inner.killed {
|
|
||||||
return Err(ZxError::BAD_STATE);
|
|
||||||
}
|
|
||||||
let child = Arc::new(Job {
|
|
||||||
base: KObjectBase::new(),
|
|
||||||
parent: Some(self.clone()),
|
|
||||||
parent_policy: inner.policy.merge(&self.parent_policy),
|
|
||||||
inner: Mutex::new(JobInner::default()),
|
|
||||||
});
|
|
||||||
let child_weak = Arc::downgrade(&child);
|
|
||||||
child.inner.lock().self_ref = child_weak.clone();
|
|
||||||
inner.children.push(child_weak);
|
|
||||||
Ok(child)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_child(&self, to_remove: &Weak<Job>) {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
inner.children.retain(|child| !to_remove.ptr_eq(child));
|
|
||||||
if inner.killed && inner.processes.is_empty() && inner.children.is_empty() {
|
|
||||||
drop(inner);
|
|
||||||
self.terminate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the policy of the job.
|
|
||||||
pub fn policy(&self) -> JobPolicy {
|
|
||||||
self.inner.lock().policy.merge(&self.parent_policy)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the parent job.
|
|
||||||
pub fn parent(&self) -> Option<Arc<Self>> {
|
|
||||||
self.parent.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets one or more security and/or resource policies to an empty job.
|
|
||||||
///
|
|
||||||
/// The job's effective policies is the combination of the parent's
|
|
||||||
/// effective policies and the policies specified in policy.
|
|
||||||
///
|
|
||||||
/// After this call succeeds any new child process or child job will have
|
|
||||||
/// the new effective policy applied to it.
|
|
||||||
pub fn set_policy_basic(
|
|
||||||
&self,
|
|
||||||
options: SetPolicyOptions,
|
|
||||||
policies: &[BasicPolicy],
|
|
||||||
) -> ZxResult {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
if !inner.is_empty() {
|
|
||||||
return Err(ZxError::BAD_STATE);
|
|
||||||
}
|
|
||||||
for policy in policies {
|
|
||||||
if self.parent_policy.get_action(policy.condition).is_some() {
|
|
||||||
match options {
|
|
||||||
SetPolicyOptions::Absolute => return Err(ZxError::ALREADY_EXISTS),
|
|
||||||
SetPolicyOptions::Relative => {}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
inner.policy.apply(*policy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a process to the job.
|
|
||||||
pub(super) fn add_process(&self, process: Arc<Process>) -> ZxResult {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
if inner.killed {
|
|
||||||
return Err(ZxError::BAD_STATE);
|
|
||||||
}
|
|
||||||
inner.processes.push(process);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove a process from the job.
|
|
||||||
pub(super) fn remove_process(&self, id: KoID) {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
inner.processes.retain(|proc| proc.id() != id);
|
|
||||||
if inner.killed && inner.processes.is_empty() && inner.children.is_empty() {
|
|
||||||
drop(inner);
|
|
||||||
self.terminate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check whether this job is root job.
|
|
||||||
pub fn check_root_job(&self) -> ZxResult {
|
|
||||||
if self.parent.is_some() {
|
|
||||||
Err(ZxError::ACCESS_DENIED)
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get KoIDs of Processes.
|
|
||||||
pub fn process_ids(&self) -> Vec<KoID> {
|
|
||||||
self.inner.lock().processes.iter().map(|p| p.id()).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get KoIDs of children Jobs.
|
|
||||||
pub fn children_ids(&self) -> Vec<KoID> {
|
|
||||||
self.inner
|
|
||||||
.lock()
|
|
||||||
.children
|
|
||||||
.iter()
|
|
||||||
.filter_map(|j| j.upgrade())
|
|
||||||
.map(|j| j.id())
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return true if this job has no processes and no child jobs.
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.inner.lock().is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The job finally terminates.
|
|
||||||
fn terminate(&self) {
|
|
||||||
if let Some(parent) = self.parent.as_ref() {
|
|
||||||
parent.remove_child(&self.inner.lock().self_ref)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Task for Job {
|
|
||||||
/// Kill the job. The job do not terminate immediately when killed.
|
|
||||||
/// It will terminate after all its children and processes are terminated.
|
|
||||||
fn kill(&self) {
|
|
||||||
let (children, processes) = {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
if inner.killed {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
inner.killed = true;
|
|
||||||
(inner.children.clone(), inner.processes.clone())
|
|
||||||
};
|
|
||||||
if children.is_empty() && processes.is_empty() {
|
|
||||||
self.terminate();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for child in children {
|
|
||||||
if let Some(child) = child.upgrade() {
|
|
||||||
child.kill();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for proc in processes {
|
|
||||||
proc.kill();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn suspend(&self) {
|
|
||||||
panic!("job do not support suspend");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resume(&self) {
|
|
||||||
panic!("job do not support resume");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl JobInner {
|
|
||||||
fn is_empty(&self) -> bool {
|
|
||||||
self.processes.is_empty() && self.children.is_empty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Job {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.terminate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::task::TASK_RETCODE_SYSCALL_KILL;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn create() {
|
|
||||||
let root_job = Job::root();
|
|
||||||
let job = Job::create_child(&root_job).expect("failed to create job");
|
|
||||||
|
|
||||||
let child = root_job
|
|
||||||
.get_child(job.id())
|
|
||||||
.unwrap()
|
|
||||||
.downcast_arc()
|
|
||||||
.unwrap();
|
|
||||||
assert!(Arc::ptr_eq(&child, &job));
|
|
||||||
assert_eq!(job.related_koid(), root_job.id());
|
|
||||||
assert_eq!(root_job.related_koid(), 0);
|
|
||||||
|
|
||||||
root_job.kill();
|
|
||||||
assert_eq!(root_job.create_child().err(), Some(ZxError::BAD_STATE));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn set_policy() {
|
|
||||||
let root_job = Job::root();
|
|
||||||
|
|
||||||
// default policy
|
|
||||||
assert_eq!(
|
|
||||||
root_job.policy().get_action(PolicyCondition::BadHandle),
|
|
||||||
None
|
|
||||||
);
|
|
||||||
|
|
||||||
// set policy for root job
|
|
||||||
let policy = &[BasicPolicy {
|
|
||||||
condition: PolicyCondition::BadHandle,
|
|
||||||
action: PolicyAction::Deny,
|
|
||||||
}];
|
|
||||||
root_job
|
|
||||||
.set_policy_basic(SetPolicyOptions::Relative, policy)
|
|
||||||
.expect("failed to set policy");
|
|
||||||
assert_eq!(
|
|
||||||
root_job.policy().get_action(PolicyCondition::BadHandle),
|
|
||||||
Some(PolicyAction::Deny)
|
|
||||||
);
|
|
||||||
|
|
||||||
// override policy should success
|
|
||||||
let policy = &[BasicPolicy {
|
|
||||||
condition: PolicyCondition::BadHandle,
|
|
||||||
action: PolicyAction::Allow,
|
|
||||||
}];
|
|
||||||
root_job
|
|
||||||
.set_policy_basic(SetPolicyOptions::Relative, policy)
|
|
||||||
.expect("failed to set policy");
|
|
||||||
assert_eq!(
|
|
||||||
root_job.policy().get_action(PolicyCondition::BadHandle),
|
|
||||||
Some(PolicyAction::Allow)
|
|
||||||
);
|
|
||||||
|
|
||||||
// create a child job
|
|
||||||
let job = Job::create_child(&root_job).expect("failed to create job");
|
|
||||||
|
|
||||||
// should inherit parent's policy.
|
|
||||||
assert_eq!(
|
|
||||||
job.policy().get_action(PolicyCondition::BadHandle),
|
|
||||||
Some(PolicyAction::Allow)
|
|
||||||
);
|
|
||||||
|
|
||||||
// setting policy for a non-empty job should fail.
|
|
||||||
assert_eq!(
|
|
||||||
root_job.set_policy_basic(SetPolicyOptions::Relative, &[]),
|
|
||||||
Err(ZxError::BAD_STATE)
|
|
||||||
);
|
|
||||||
|
|
||||||
// set new policy should success.
|
|
||||||
let policy = &[BasicPolicy {
|
|
||||||
condition: PolicyCondition::WrongObject,
|
|
||||||
action: PolicyAction::Allow,
|
|
||||||
}];
|
|
||||||
job.set_policy_basic(SetPolicyOptions::Relative, policy)
|
|
||||||
.expect("failed to set policy");
|
|
||||||
assert_eq!(
|
|
||||||
job.policy().get_action(PolicyCondition::WrongObject),
|
|
||||||
Some(PolicyAction::Allow)
|
|
||||||
);
|
|
||||||
|
|
||||||
// relatively setting existing policy should be ignored.
|
|
||||||
let policy = &[BasicPolicy {
|
|
||||||
condition: PolicyCondition::BadHandle,
|
|
||||||
action: PolicyAction::Deny,
|
|
||||||
}];
|
|
||||||
job.set_policy_basic(SetPolicyOptions::Relative, policy)
|
|
||||||
.expect("failed to set policy");
|
|
||||||
assert_eq!(
|
|
||||||
job.policy().get_action(PolicyCondition::BadHandle),
|
|
||||||
Some(PolicyAction::Allow)
|
|
||||||
);
|
|
||||||
|
|
||||||
// absolutely setting existing policy should fail.
|
|
||||||
assert_eq!(
|
|
||||||
job.set_policy_basic(SetPolicyOptions::Absolute, policy),
|
|
||||||
Err(ZxError::ALREADY_EXISTS)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parent_child() {
|
|
||||||
let root_job = Job::root();
|
|
||||||
let job = Job::create_child(&root_job).expect("failed to create job");
|
|
||||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
|
||||||
|
|
||||||
assert_eq!(root_job.get_child(job.id()).unwrap().id(), job.id());
|
|
||||||
assert_eq!(root_job.get_child(proc.id()).unwrap().id(), proc.id());
|
|
||||||
assert_eq!(
|
|
||||||
root_job.get_child(root_job.id()).err(),
|
|
||||||
Some(ZxError::NOT_FOUND)
|
|
||||||
);
|
|
||||||
assert!(Arc::ptr_eq(&job.parent().unwrap(), &root_job));
|
|
||||||
|
|
||||||
let job1 = root_job.create_child().expect("failed to create job");
|
|
||||||
let proc1 = Process::create(&root_job, "proc1").expect("failed to create process");
|
|
||||||
assert_eq!(root_job.children_ids(), vec![job.id(), job1.id()]);
|
|
||||||
assert_eq!(root_job.process_ids(), vec![proc.id(), proc1.id()]);
|
|
||||||
|
|
||||||
root_job.kill();
|
|
||||||
assert_eq!(root_job.create_child().err(), Some(ZxError::BAD_STATE));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn check() {
|
|
||||||
let root_job = Job::root();
|
|
||||||
assert!(root_job.is_empty());
|
|
||||||
let job = root_job.create_child().expect("failed to create job");
|
|
||||||
assert_eq!(root_job.check_root_job(), Ok(()));
|
|
||||||
assert_eq!(job.check_root_job(), Err(ZxError::ACCESS_DENIED));
|
|
||||||
|
|
||||||
assert!(!root_job.is_empty());
|
|
||||||
assert!(job.is_empty());
|
|
||||||
|
|
||||||
let _proc = Process::create(&job, "proc").expect("failed to create process");
|
|
||||||
assert!(!job.is_empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn kill() {
|
|
||||||
let root_job = Job::root();
|
|
||||||
let job = Job::create_child(&root_job).expect("failed to create job");
|
|
||||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
|
||||||
let thread = Thread::create(&proc, "thread").expect("failed to create thread");
|
|
||||||
let current_thread = CurrentThread(thread.clone());
|
|
||||||
|
|
||||||
root_job.kill();
|
|
||||||
assert!(root_job.inner.lock().killed);
|
|
||||||
assert!(job.inner.lock().killed);
|
|
||||||
assert_eq!(proc.status(), Status::Exited(TASK_RETCODE_SYSCALL_KILL));
|
|
||||||
assert_eq!(thread.state(), ThreadState::Dying);
|
|
||||||
|
|
||||||
std::mem::drop(current_thread);
|
|
||||||
assert!(root_job.inner.lock().killed);
|
|
||||||
assert!(job.inner.lock().killed);
|
|
||||||
assert_eq!(proc.status(), Status::Exited(TASK_RETCODE_SYSCALL_KILL));
|
|
||||||
assert_eq!(thread.state(), ThreadState::Dead);
|
|
||||||
|
|
||||||
// The job has no children.
|
|
||||||
let root_job = Job::root();
|
|
||||||
root_job.kill();
|
|
||||||
assert!(root_job.inner.lock().killed);
|
|
||||||
|
|
||||||
// The job's process have no threads.
|
|
||||||
let root_job = Job::root();
|
|
||||||
let job = Job::create_child(&root_job).expect("failed to create job");
|
|
||||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
|
||||||
root_job.kill();
|
|
||||||
assert!(root_job.inner.lock().killed);
|
|
||||||
assert!(job.inner.lock().killed);
|
|
||||||
assert_eq!(proc.status(), Status::Exited(TASK_RETCODE_SYSCALL_KILL));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,108 +0,0 @@
|
|||||||
/// Security and resource policies of a job.
|
|
||||||
#[derive(Default, Copy, Clone)]
|
|
||||||
pub struct JobPolicy {
|
|
||||||
// TODO: use bitset
|
|
||||||
action: [Option<PolicyAction>; 15],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl JobPolicy {
|
|
||||||
/// Get the action of a policy `condition`.
|
|
||||||
pub fn get_action(&self, condition: PolicyCondition) -> Option<PolicyAction> {
|
|
||||||
self.action[condition as usize]
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Apply a basic policy.
|
|
||||||
pub fn apply(&mut self, policy: BasicPolicy) {
|
|
||||||
self.action[policy.condition as usize] = Some(policy.action);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Merge the policy with `parent`'s.
|
|
||||||
pub fn merge(&self, parent: &Self) -> Self {
|
|
||||||
let mut new = *self;
|
|
||||||
for i in 0..15 {
|
|
||||||
if parent.action[i].is_some() {
|
|
||||||
new.action[i] = parent.action[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
new
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Control the effect in the case of conflict between
|
|
||||||
/// the existing policies and the new policies when setting new policies.
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub enum SetPolicyOptions {
|
|
||||||
/// Policy is applied for all conditions in policy or the call fails.
|
|
||||||
Absolute,
|
|
||||||
/// Policy is applied for the conditions not specifically overridden by the parent policy.
|
|
||||||
Relative,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The policy type.
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub struct BasicPolicy {
|
|
||||||
/// Condition when the policy is applied.
|
|
||||||
pub condition: PolicyCondition,
|
|
||||||
///
|
|
||||||
pub action: PolicyAction,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The condition when a policy is applied.
|
|
||||||
#[repr(u32)]
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub enum PolicyCondition {
|
|
||||||
/// A process under this job is attempting to issue a syscall with an invalid handle.
|
|
||||||
/// In this case, `PolicyAction::Allow` and `PolicyAction::Deny` are equivalent:
|
|
||||||
/// if the syscall returns, it will always return the error ZX_ERR_BAD_HANDLE.
|
|
||||||
BadHandle = 0,
|
|
||||||
/// A process under this job is attempting to issue a syscall with a handle that does not support such operation.
|
|
||||||
WrongObject = 1,
|
|
||||||
/// A process under this job is attempting to map an address region with write-execute access.
|
|
||||||
VmarWx = 2,
|
|
||||||
/// A special condition that stands for all of the above ZX_NEW conditions
|
|
||||||
/// such as NEW_VMO, NEW_CHANNEL, NEW_EVENT, NEW_EVENTPAIR, NEW_PORT, NEW_SOCKET, NEW_FIFO,
|
|
||||||
/// And any future ZX_NEW policy.
|
|
||||||
/// This will include any new kernel objects which do not require a parent object for creation.
|
|
||||||
NewAny = 3,
|
|
||||||
/// A process under this job is attempting to create a new vm object.
|
|
||||||
NewVMO = 4,
|
|
||||||
/// A process under this job is attempting to create a new channel.
|
|
||||||
NewChannel = 5,
|
|
||||||
/// A process under this job is attempting to create a new event.
|
|
||||||
NewEvent = 6,
|
|
||||||
/// A process under this job is attempting to create a new event pair.
|
|
||||||
NewEventPair = 7,
|
|
||||||
/// A process under this job is attempting to create a new port.
|
|
||||||
NewPort = 8,
|
|
||||||
/// A process under this job is attempting to create a new socket.
|
|
||||||
NewSocket = 9,
|
|
||||||
/// A process under this job is attempting to create a new fifo.
|
|
||||||
NewFIFO = 10,
|
|
||||||
/// A process under this job is attempting to create a new timer.
|
|
||||||
NewTimer = 11,
|
|
||||||
/// A process under this job is attempting to create a new process.
|
|
||||||
NewProcess = 12,
|
|
||||||
/// A process under this job is attempting to create a new profile.
|
|
||||||
NewProfile = 13,
|
|
||||||
/// A process under this job is attempting to use zx_vmo_replace_as_executable()
|
|
||||||
/// with a ZX_HANDLE_INVALID as the second argument rather than a valid ZX_RSRC_KIND_VMEX.
|
|
||||||
AmbientMarkVMOExec = 14,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The action taken when the condition happens specified by a policy.
|
|
||||||
#[repr(u32)]
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
|
||||||
pub enum PolicyAction {
|
|
||||||
/// Allow condition.
|
|
||||||
Allow = 0,
|
|
||||||
/// Prevent condition.
|
|
||||||
Deny = 1,
|
|
||||||
/// Generate an exception via the debug port. An exception generated this
|
|
||||||
/// way acts as a breakpoint. The thread may be resumed after the exception.
|
|
||||||
AllowException = 2,
|
|
||||||
/// Just like `AllowException`, but after resuming condition is denied.
|
|
||||||
DenyException = 3,
|
|
||||||
/// Terminate the process.
|
|
||||||
Kill = 4,
|
|
||||||
}
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
use super::*;
|
|
||||||
|
|
||||||
mod job;
|
|
||||||
mod job_policy;
|
|
||||||
mod process;
|
|
||||||
mod thread;
|
|
||||||
|
|
||||||
pub use {self::job::*, self::job_policy::*, self::process::*, self::thread::*};
|
|
||||||
|
|
||||||
/// Task (Thread, Process, or Job)
|
|
||||||
pub trait Task: Sync + Send {
|
|
||||||
/// Kill the task. The task do not terminate immediately when killed.
|
|
||||||
/// It will terminate after all its children are terminated or some cleanups are finished.
|
|
||||||
fn kill(&self);
|
|
||||||
|
|
||||||
/// Suspend the task. Currently only thread or process handles may be suspended.
|
|
||||||
fn suspend(&self);
|
|
||||||
|
|
||||||
/// Resume the task
|
|
||||||
fn resume(&self);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The return code set when a task is killed via zx_task_kill().
|
|
||||||
pub const TASK_RETCODE_SYSCALL_KILL: i64 = -1028;
|
|
||||||
@ -1,505 +0,0 @@
|
|||||||
use {
|
|
||||||
super::{job::Job, job_policy::*, thread::*, *},
|
|
||||||
crate::{error::*, object::*, vm::*},
|
|
||||||
alloc::{sync::Arc, vec::Vec},
|
|
||||||
hashbrown::HashMap,
|
|
||||||
spin::Mutex,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct Process {
|
|
||||||
base: KObjectBase,
|
|
||||||
job: Arc<Job>,
|
|
||||||
policy: JobPolicy,
|
|
||||||
vmar: Arc<VmAddressRegion>,
|
|
||||||
inner: Mutex<ProcessInner>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_kobject!(Process
|
|
||||||
fn get_child(&self, id: KoID) -> ZxResult<Arc<dyn KernelObject>> {
|
|
||||||
let inner = self.inner.lock();
|
|
||||||
let thread = inner.threads.iter().find(|o| o.id() == id).ok_or(ZxError::NOT_FOUND)?;
|
|
||||||
Ok(thread.clone())
|
|
||||||
}
|
|
||||||
fn related_koid(&self) -> KoID {
|
|
||||||
self.job.id()
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct ProcessInner {
|
|
||||||
max_handle_id: u32,
|
|
||||||
status: Status,
|
|
||||||
handles: HashMap<HandleValue, Handle>,
|
|
||||||
threads: Vec<Arc<Thread>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Status of a process.
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
|
||||||
pub enum Status {
|
|
||||||
/// Initial state, no thread present in process.
|
|
||||||
Init,
|
|
||||||
/// First thread has started and is running.
|
|
||||||
Running,
|
|
||||||
/// Process has exited with the code.
|
|
||||||
Exited(i64),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Status {
|
|
||||||
fn default() -> Self {
|
|
||||||
Status::Init
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Process {
|
|
||||||
/// Create a new process in the `job`.
|
|
||||||
pub fn create(job: &Arc<Job>, name: &str) -> ZxResult<Arc<Self>> {
|
|
||||||
let proc = Arc::new(Process {
|
|
||||||
base: KObjectBase::with_name(name),
|
|
||||||
job: job.clone(),
|
|
||||||
policy: job.policy(),
|
|
||||||
vmar: VmAddressRegion::new_root(),
|
|
||||||
inner: Mutex::new(ProcessInner::default()),
|
|
||||||
});
|
|
||||||
job.add_process(proc.clone())?;
|
|
||||||
Ok(proc)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a handle from the process
|
|
||||||
fn get_handle(&self, handle_value: HandleValue) -> ZxResult<Handle> {
|
|
||||||
self.inner.lock().get_handle(handle_value)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 添加一个新的对象句柄
|
|
||||||
pub fn add_handle(&self, handle: Handle) -> HandleValue {
|
|
||||||
self.inner.lock().add_handle(handle)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 删除一个对象句柄
|
|
||||||
pub fn remove_handle(&self, handle_value: HandleValue) -> ZxResult<Handle> {
|
|
||||||
self.inner.lock().remove_handle(handle_value)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add all handles to the process
|
|
||||||
pub fn add_handles(&self, handles: Vec<Handle>) -> Vec<HandleValue> {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
handles.into_iter().map(|h| inner.add_handle(h)).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove all handles from the process.
|
|
||||||
pub fn remove_handles(&self, handle_values: &[HandleValue]) -> ZxResult<Vec<Handle>> {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
handle_values
|
|
||||||
.iter()
|
|
||||||
.map(|h| inner.remove_handle(*h))
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the kernel object corresponding to this `handle_value`
|
|
||||||
pub fn get_object<T: KernelObject>(&self, handle_value: HandleValue) -> ZxResult<Arc<T>> {
|
|
||||||
let handle = self.get_handle(handle_value)?;
|
|
||||||
let object = handle
|
|
||||||
.object
|
|
||||||
.downcast_arc::<T>()
|
|
||||||
.map_err(|_| ZxError::WRONG_TYPE)?;
|
|
||||||
Ok(object)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 根据句柄值查找内核对象,并检查权限
|
|
||||||
pub fn get_object_with_rights<T: KernelObject>(
|
|
||||||
&self,
|
|
||||||
handle_value: HandleValue,
|
|
||||||
desired_rights: Rights,
|
|
||||||
) -> ZxResult<Arc<T>> {
|
|
||||||
let handle = self.get_handle(handle_value)?;
|
|
||||||
// check type before rights
|
|
||||||
let object = handle
|
|
||||||
.object
|
|
||||||
.downcast_arc::<T>()
|
|
||||||
.map_err(|_| ZxError::WRONG_TYPE)?;
|
|
||||||
if !handle.rights.contains(desired_rights) {
|
|
||||||
return Err(ZxError::ACCESS_DENIED);
|
|
||||||
}
|
|
||||||
Ok(object)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the kernel object corresponding to this `handle_value` and this handle's rights.
|
|
||||||
pub fn get_object_and_rights<T: KernelObject>(
|
|
||||||
&self,
|
|
||||||
handle_value: HandleValue,
|
|
||||||
) -> ZxResult<(Arc<T>, Rights)> {
|
|
||||||
let handle = self.get_handle(handle_value)?;
|
|
||||||
let object = handle
|
|
||||||
.object
|
|
||||||
.downcast_arc::<T>()
|
|
||||||
.map_err(|_| ZxError::WRONG_TYPE)?;
|
|
||||||
Ok((object, handle.rights))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove a handle referring to a kernel object of the given type from the process.
|
|
||||||
pub fn remove_object<T: KernelObject>(&self, handle_value: HandleValue) -> ZxResult<Arc<T>> {
|
|
||||||
let handle = self.remove_handle(handle_value)?;
|
|
||||||
let object = handle
|
|
||||||
.object
|
|
||||||
.downcast_arc::<T>()
|
|
||||||
.map_err(|_| ZxError::WRONG_TYPE)?;
|
|
||||||
Ok(object)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn start(
|
|
||||||
&self,
|
|
||||||
thread: &Arc<Thread>,
|
|
||||||
entry: usize,
|
|
||||||
stack: usize,
|
|
||||||
arg1: Option<Handle>,
|
|
||||||
arg2: usize,
|
|
||||||
thread_fn: ThreadFn,
|
|
||||||
) -> ZxResult {
|
|
||||||
let handle_value;
|
|
||||||
{
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
if !inner.contains_thread(thread) {
|
|
||||||
return Err(ZxError::ACCESS_DENIED);
|
|
||||||
}
|
|
||||||
if inner.status != Status::Init {
|
|
||||||
return Err(ZxError::BAD_STATE);
|
|
||||||
}
|
|
||||||
inner.status = Status::Running;
|
|
||||||
handle_value = arg1.map_or(INVALID_HANDLE, |handle| inner.add_handle(handle));
|
|
||||||
}
|
|
||||||
thread.set_first_thread();
|
|
||||||
match thread.start(entry, stack, handle_value as usize, arg2, thread_fn) {
|
|
||||||
Ok(_) => Ok(()),
|
|
||||||
Err(err) => {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
if handle_value != INVALID_HANDLE {
|
|
||||||
inner.remove_handle(handle_value).ok();
|
|
||||||
}
|
|
||||||
Err(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Exit current process with `retcode`.
|
|
||||||
/// The process do not terminate immediately when exited.
|
|
||||||
/// It will terminate after all its child threads are terminated.
|
|
||||||
pub fn exit(&self, retcode: i64) {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
if let Status::Exited(_) = inner.status {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
inner.status = Status::Exited(retcode);
|
|
||||||
if inner.threads.is_empty() {
|
|
||||||
inner.handles.clear();
|
|
||||||
drop(inner);
|
|
||||||
self.terminate();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for thread in inner.threads.iter() {
|
|
||||||
thread.kill();
|
|
||||||
}
|
|
||||||
inner.handles.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The process finally terminates.
|
|
||||||
fn terminate(&self) {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
let _retcode = match inner.status {
|
|
||||||
Status::Exited(retcode) => retcode,
|
|
||||||
_ => {
|
|
||||||
inner.status = Status::Exited(0);
|
|
||||||
0
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.job.remove_process(self.base.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check whether `condition` is allowed in the parent job's policy.
|
|
||||||
pub fn check_policy(&self, condition: PolicyCondition) -> ZxResult {
|
|
||||||
match self
|
|
||||||
.policy
|
|
||||||
.get_action(condition)
|
|
||||||
.unwrap_or(PolicyAction::Allow)
|
|
||||||
{
|
|
||||||
PolicyAction::Allow => Ok(()),
|
|
||||||
PolicyAction::Deny => Err(ZxError::ACCESS_DENIED),
|
|
||||||
_ => unimplemented!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get process status.
|
|
||||||
pub fn status(&self) -> Status {
|
|
||||||
self.inner.lock().status
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the `VmAddressRegion` of the process.
|
|
||||||
pub fn vmar(&self) -> Arc<VmAddressRegion> {
|
|
||||||
self.vmar.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the job of the process.
|
|
||||||
pub fn job(&self) -> Arc<Job> {
|
|
||||||
self.job.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a thread to the process.
|
|
||||||
pub(super) fn add_thread(&self, thread: Arc<Thread>) -> ZxResult {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
if let Status::Exited(_) = inner.status {
|
|
||||||
return Err(ZxError::BAD_STATE);
|
|
||||||
}
|
|
||||||
inner.threads.push(thread);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove a thread from the process.
|
|
||||||
///
|
|
||||||
/// If no more threads left, exit the process.
|
|
||||||
pub(super) fn remove_thread(&self, tid: KoID) {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
inner.threads.retain(|t| t.id() != tid);
|
|
||||||
if inner.threads.is_empty() {
|
|
||||||
drop(inner);
|
|
||||||
self.terminate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get KoIDs of Threads.
|
|
||||||
pub fn thread_ids(&self) -> Vec<KoID> {
|
|
||||||
self.inner.lock().threads.iter().map(|t| t.id()).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get information of this process.
|
|
||||||
pub fn get_info(&self) -> ProcessInfo {
|
|
||||||
let mut info = ProcessInfo {
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
match self.inner.lock().status {
|
|
||||||
Status::Init => {
|
|
||||||
info.started = false;
|
|
||||||
info.has_exited = false;
|
|
||||||
}
|
|
||||||
Status::Running => {
|
|
||||||
info.started = true;
|
|
||||||
info.has_exited = false;
|
|
||||||
}
|
|
||||||
Status::Exited(ret) => {
|
|
||||||
info.return_code = ret;
|
|
||||||
info.has_exited = true;
|
|
||||||
info.started = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
info
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Information of a process.
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct ProcessInfo {
|
|
||||||
pub return_code: i64,
|
|
||||||
pub started: bool,
|
|
||||||
pub has_exited: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Task for Process {
|
|
||||||
fn kill(&self) {
|
|
||||||
self.exit(TASK_RETCODE_SYSCALL_KILL);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn suspend(&self) {
|
|
||||||
let inner = self.inner.lock();
|
|
||||||
for thread in inner.threads.iter() {
|
|
||||||
thread.suspend();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resume(&self) {
|
|
||||||
let inner = self.inner.lock();
|
|
||||||
for thread in inner.threads.iter() {
|
|
||||||
thread.resume();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ProcessInner {
|
|
||||||
/// Add a handle to the process
|
|
||||||
fn add_handle(&mut self, handle: Handle) -> HandleValue {
|
|
||||||
let key = (self.max_handle_id << 2) | 0x3u32;
|
|
||||||
self.max_handle_id += 1;
|
|
||||||
self.handles.insert(key, handle);
|
|
||||||
key
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_handle(&mut self, handle_value: HandleValue) -> ZxResult<Handle> {
|
|
||||||
let handle = self
|
|
||||||
.handles
|
|
||||||
.remove(&handle_value)
|
|
||||||
.ok_or(ZxError::BAD_HANDLE)?;
|
|
||||||
Ok(handle)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_handle(&mut self, handle_value: HandleValue) -> ZxResult<Handle> {
|
|
||||||
let handle = self.handles.get(&handle_value).ok_or(ZxError::BAD_HANDLE)?;
|
|
||||||
Ok(handle.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether `thread` is in this process.
|
|
||||||
fn contains_thread(&self, thread: &Arc<Thread>) -> bool {
|
|
||||||
self.threads.iter().any(|t| Arc::ptr_eq(t, thread))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn create() {
|
|
||||||
let root_job = Job::root();
|
|
||||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
|
||||||
|
|
||||||
assert_eq!(proc.related_koid(), root_job.id());
|
|
||||||
assert!(Arc::ptr_eq(&root_job, &proc.job()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn handle() {
|
|
||||||
let root_job = Job::root();
|
|
||||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
|
||||||
let handle = Handle::new(proc.clone(), Rights::DEFAULT_PROCESS);
|
|
||||||
|
|
||||||
let handle_value = proc.add_handle(handle);
|
|
||||||
|
|
||||||
// getting object should success
|
|
||||||
let object: Arc<Process> = proc
|
|
||||||
.get_object_with_rights(handle_value, Rights::DEFAULT_PROCESS)
|
|
||||||
.expect("failed to get object");
|
|
||||||
assert!(Arc::ptr_eq(&object, &proc));
|
|
||||||
|
|
||||||
let (object, rights) = proc
|
|
||||||
.get_object_and_rights::<Process>(handle_value)
|
|
||||||
.expect("failed to get object");
|
|
||||||
assert!(Arc::ptr_eq(&object, &proc));
|
|
||||||
assert_eq!(rights, Rights::DEFAULT_PROCESS);
|
|
||||||
|
|
||||||
// getting object with an extra rights should fail.
|
|
||||||
assert_eq!(
|
|
||||||
proc.get_object_with_rights::<Process>(handle_value, Rights::MANAGE_JOB)
|
|
||||||
.err(),
|
|
||||||
Some(ZxError::ACCESS_DENIED)
|
|
||||||
);
|
|
||||||
|
|
||||||
// getting object with invalid type should fail.
|
|
||||||
assert_eq!(
|
|
||||||
proc.get_object_with_rights::<Job>(handle_value, Rights::DEFAULT_PROCESS)
|
|
||||||
.err(),
|
|
||||||
Some(ZxError::WRONG_TYPE)
|
|
||||||
);
|
|
||||||
|
|
||||||
proc.remove_handle(handle_value).unwrap();
|
|
||||||
|
|
||||||
// getting object with invalid handle should fail.
|
|
||||||
assert_eq!(
|
|
||||||
proc.get_object_with_rights::<Process>(handle_value, Rights::DEFAULT_PROCESS)
|
|
||||||
.err(),
|
|
||||||
Some(ZxError::BAD_HANDLE)
|
|
||||||
);
|
|
||||||
|
|
||||||
let handle1 = Handle::new(proc.clone(), Rights::DEFAULT_PROCESS);
|
|
||||||
let handle2 = Handle::new(proc.clone(), Rights::DEFAULT_PROCESS);
|
|
||||||
|
|
||||||
let handle_values = proc.add_handles(vec![handle1, handle2]);
|
|
||||||
let object1: Arc<Process> = proc
|
|
||||||
.get_object_with_rights(handle_values[0], Rights::DEFAULT_PROCESS)
|
|
||||||
.expect("failed to get object");
|
|
||||||
assert!(Arc::ptr_eq(&object1, &proc));
|
|
||||||
|
|
||||||
proc.remove_handles(&handle_values).unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
proc.get_object_with_rights::<Process>(handle_values[0], Rights::DEFAULT_PROCESS)
|
|
||||||
.err(),
|
|
||||||
Some(ZxError::BAD_HANDLE)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_child() {
|
|
||||||
let root_job = Job::root();
|
|
||||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
|
||||||
let thread = Thread::create(&proc, "thread").expect("failed to create thread");
|
|
||||||
|
|
||||||
assert_eq!(proc.get_child(thread.id()).unwrap().id(), thread.id());
|
|
||||||
assert_eq!(proc.get_child(proc.id()).err(), Some(ZxError::NOT_FOUND));
|
|
||||||
|
|
||||||
let thread1 = Thread::create(&proc, "thread1").expect("failed to create thread");
|
|
||||||
assert_eq!(proc.thread_ids(), vec![thread.id(), thread1.id()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn contains_thread() {
|
|
||||||
let root_job = Job::root();
|
|
||||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
|
||||||
let thread = Thread::create(&proc, "thread").expect("failed to create thread");
|
|
||||||
|
|
||||||
let proc1 = Process::create(&root_job, "proc1").expect("failed to create process");
|
|
||||||
let thread1 = Thread::create(&proc1, "thread1").expect("failed to create thread");
|
|
||||||
|
|
||||||
let inner = proc.inner.lock();
|
|
||||||
assert!(inner.contains_thread(&thread) && !inner.contains_thread(&thread1));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn check_policy() {
|
|
||||||
let root_job = Job::root();
|
|
||||||
let policy1 = BasicPolicy {
|
|
||||||
condition: PolicyCondition::BadHandle,
|
|
||||||
action: PolicyAction::Allow,
|
|
||||||
};
|
|
||||||
let policy2 = BasicPolicy {
|
|
||||||
condition: PolicyCondition::NewChannel,
|
|
||||||
action: PolicyAction::Deny,
|
|
||||||
};
|
|
||||||
|
|
||||||
assert!(root_job
|
|
||||||
.set_policy_basic(SetPolicyOptions::Absolute, &[policy1, policy2])
|
|
||||||
.is_ok());
|
|
||||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
|
||||||
|
|
||||||
assert!(proc.check_policy(PolicyCondition::BadHandle).is_ok());
|
|
||||||
assert!(proc.check_policy(PolicyCondition::NewProcess).is_ok());
|
|
||||||
assert_eq!(
|
|
||||||
proc.check_policy(PolicyCondition::NewChannel).err(),
|
|
||||||
Some(ZxError::ACCESS_DENIED)
|
|
||||||
);
|
|
||||||
|
|
||||||
let _job = root_job.create_child().unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
root_job
|
|
||||||
.set_policy_basic(SetPolicyOptions::Absolute, &[policy1, policy2])
|
|
||||||
.err(),
|
|
||||||
Some(ZxError::BAD_STATE)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn exit() {
|
|
||||||
let root_job = Job::root();
|
|
||||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
|
||||||
let thread = Thread::create(&proc, "thread").expect("failed to create thread");
|
|
||||||
|
|
||||||
let info = proc.get_info();
|
|
||||||
assert!(!info.has_exited && !info.started && info.return_code == 0);
|
|
||||||
|
|
||||||
proc.exit(666);
|
|
||||||
let info = proc.get_info();
|
|
||||||
assert!(info.has_exited && info.started && info.return_code == 666);
|
|
||||||
assert_eq!(thread.state(), ThreadState::Dying);
|
|
||||||
// TODO: when is the thread dead?
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
Thread::create(&proc, "thread1").err(),
|
|
||||||
Some(ZxError::BAD_STATE)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,567 +0,0 @@
|
|||||||
use {
|
|
||||||
super::process::Process,
|
|
||||||
super::*,
|
|
||||||
crate::object::*,
|
|
||||||
alloc::{boxed::Box, sync::Arc},
|
|
||||||
bitflags::bitflags,
|
|
||||||
core::{
|
|
||||||
future::Future,
|
|
||||||
ops::Deref,
|
|
||||||
pin::Pin,
|
|
||||||
task::{Context, Poll, Waker},
|
|
||||||
},
|
|
||||||
spin::Mutex,
|
|
||||||
trapframe::UserContext,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use self::thread_state::*;
|
|
||||||
|
|
||||||
mod thread_state;
|
|
||||||
|
|
||||||
pub struct Thread {
|
|
||||||
base: KObjectBase,
|
|
||||||
proc: Arc<Process>,
|
|
||||||
inner: Mutex<ThreadInner>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_kobject!(Thread
|
|
||||||
fn related_koid(&self) -> KoID {
|
|
||||||
self.proc.id()
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct ThreadInner {
|
|
||||||
/// Thread context
|
|
||||||
///
|
|
||||||
/// It will be taken away when running this thread.
|
|
||||||
context: Option<Box<UserContext>>,
|
|
||||||
/// The number of existing `SuspendToken`.
|
|
||||||
suspend_count: usize,
|
|
||||||
/// The waker of task when suspending.
|
|
||||||
waker: Option<Waker>,
|
|
||||||
/// Thread state
|
|
||||||
///
|
|
||||||
/// NOTE: This variable will never be `Suspended`. On suspended, the
|
|
||||||
/// `suspend_count` is non-zero, and this represents the state before suspended.
|
|
||||||
state: ThreadState,
|
|
||||||
/// Should The ProcessStarting exception generated at start of this thread
|
|
||||||
first_thread: bool,
|
|
||||||
/// Should The ThreadExiting exception do not block this thread
|
|
||||||
killed: bool,
|
|
||||||
/// The time this thread has run on cpu
|
|
||||||
time: u128,
|
|
||||||
flags: ThreadFlag,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ThreadInner {
|
|
||||||
fn state(&self) -> ThreadState {
|
|
||||||
// Dying > Exception > Suspend > Blocked
|
|
||||||
if self.suspend_count == 0
|
|
||||||
|| self.context.is_none()
|
|
||||||
|| self.state == ThreadState::BlockedException
|
|
||||||
|| self.state == ThreadState::Dying
|
|
||||||
|| self.state == ThreadState::Dead
|
|
||||||
{
|
|
||||||
self.state
|
|
||||||
} else {
|
|
||||||
ThreadState::Suspended
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Change state and update signal.
|
|
||||||
fn change_state(&mut self, state: ThreadState) {
|
|
||||||
self.state = state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bitflags! {
|
|
||||||
/// Thread flags.
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct ThreadFlag: usize {
|
|
||||||
/// The thread currently has a VCPU.
|
|
||||||
const VCPU = 1 << 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The type of a new thread function.
|
|
||||||
pub type ThreadFn = fn(thread: CurrentThread) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>>;
|
|
||||||
|
|
||||||
impl Thread {
|
|
||||||
/// Create a new thread.
|
|
||||||
pub fn create(proc: &Arc<Process>, name: &str) -> ZxResult<Arc<Self>> {
|
|
||||||
let thread = Arc::new(Thread {
|
|
||||||
base: KObjectBase::with_name(name),
|
|
||||||
proc: proc.clone(),
|
|
||||||
inner: Mutex::new(ThreadInner {
|
|
||||||
context: Some(Box::new(UserContext::default())),
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
proc.add_thread(thread.clone())?;
|
|
||||||
Ok(thread)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the process.
|
|
||||||
pub fn proc(&self) -> &Arc<Process> {
|
|
||||||
&self.proc
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Start execution on the thread.
|
|
||||||
pub fn start(
|
|
||||||
self: &Arc<Self>,
|
|
||||||
entry: usize,
|
|
||||||
stack: usize,
|
|
||||||
arg1: usize,
|
|
||||||
arg2: usize,
|
|
||||||
thread_fn: ThreadFn,
|
|
||||||
) -> ZxResult {
|
|
||||||
{
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
let context = inner.context.as_mut().ok_or(ZxError::BAD_STATE)?;
|
|
||||||
context.general.rip = entry;
|
|
||||||
context.general.rsp = stack;
|
|
||||||
context.general.rdi = arg1;
|
|
||||||
context.general.rsi = arg2;
|
|
||||||
context.general.rflags |= 0x3202;
|
|
||||||
inner.change_state(ThreadState::Running);
|
|
||||||
}
|
|
||||||
kernel_hal::Thread::spawn(thread_fn(CurrentThread(self.clone())), 0);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stop the thread. Internal implementation of `exit` and `kill`.
|
|
||||||
///
|
|
||||||
/// The thread do not terminate immediately when stopped. It is just made dying.
|
|
||||||
/// It will terminate after some cleanups (when `terminate` are called **explicitly** by upper layer).
|
|
||||||
fn stop(&self, killed: bool) {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
if inner.state == ThreadState::Dead {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if killed {
|
|
||||||
inner.killed = true;
|
|
||||||
}
|
|
||||||
if inner.state == ThreadState::Dying {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
inner.change_state(ThreadState::Dying);
|
|
||||||
if let Some(waker) = inner.waker.take() {
|
|
||||||
waker.wake();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read one aspect of thread state.
|
|
||||||
pub fn read_state(&self, kind: ThreadStateKind, buf: &mut [u8]) -> ZxResult<usize> {
|
|
||||||
let inner = self.inner.lock();
|
|
||||||
let state = inner.state();
|
|
||||||
if state != ThreadState::BlockedException && state != ThreadState::Suspended {
|
|
||||||
return Err(ZxError::BAD_STATE);
|
|
||||||
}
|
|
||||||
let context = inner.context.as_ref().ok_or(ZxError::BAD_STATE)?;
|
|
||||||
context.read_state(kind, buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write one aspect of thread state.
|
|
||||||
pub fn write_state(&self, kind: ThreadStateKind, buf: &[u8]) -> ZxResult {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
let state = inner.state();
|
|
||||||
if state != ThreadState::BlockedException && state != ThreadState::Suspended {
|
|
||||||
return Err(ZxError::BAD_STATE);
|
|
||||||
}
|
|
||||||
let context = inner.context.as_mut().ok_or(ZxError::BAD_STATE)?;
|
|
||||||
context.write_state(kind, buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the thread's information.
|
|
||||||
pub fn get_thread_info(&self) -> ThreadInfo {
|
|
||||||
let inner = self.inner.lock();
|
|
||||||
ThreadInfo {
|
|
||||||
state: inner.state() as u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Get the thread state.
|
|
||||||
pub fn state(&self) -> ThreadState {
|
|
||||||
self.inner.lock().state()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add the parameter to the time this thread has run on cpu.
|
|
||||||
pub fn time_add(&self, time: u128) {
|
|
||||||
self.inner.lock().time += time;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the time this thread has run on cpu.
|
|
||||||
pub fn get_time(&self) -> u64 {
|
|
||||||
self.inner.lock().time as u64
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set this thread as the first thread of a process.
|
|
||||||
pub(super) fn set_first_thread(&self) {
|
|
||||||
self.inner.lock().first_thread = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether this thread is the first thread of a process.
|
|
||||||
pub fn is_first_thread(&self) -> bool {
|
|
||||||
self.inner.lock().first_thread
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the thread's flags.
|
|
||||||
pub fn flags(&self) -> ThreadFlag {
|
|
||||||
self.inner.lock().flags
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Apply `f` to the thread's flags.
|
|
||||||
pub fn update_flags(&self, f: impl FnOnce(&mut ThreadFlag)) {
|
|
||||||
f(&mut self.inner.lock().flags)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the thread local fsbase register on x86_64.
|
|
||||||
pub fn set_fsbase(&self, fsbase: usize) -> ZxResult {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
let context = inner.context.as_mut().ok_or(ZxError::BAD_STATE)?;
|
|
||||||
context.general.fsbase = fsbase;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the thread local gsbase register on x86_64.
|
|
||||||
pub fn set_gsbase(&self, gsbase: usize) -> ZxResult {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
let context = inner.context.as_mut().ok_or(ZxError::BAD_STATE)?;
|
|
||||||
context.general.gsbase = gsbase;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Task for Thread {
|
|
||||||
fn kill(&self) {
|
|
||||||
self.stop(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn suspend(&self) {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
inner.suspend_count += 1;
|
|
||||||
// let state = inner.state;
|
|
||||||
// inner.change_state(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resume(&self) {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
assert_ne!(inner.suspend_count, 0);
|
|
||||||
inner.suspend_count -= 1;
|
|
||||||
if inner.suspend_count == 0 {
|
|
||||||
// let state = inner.state;
|
|
||||||
// inner.change_state(state);
|
|
||||||
if let Some(waker) = inner.waker.take() {
|
|
||||||
waker.wake();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A handle to current thread.
|
|
||||||
///
|
|
||||||
/// This is a wrapper of [`Thread`] that provides additional methods for the thread runner.
|
|
||||||
/// It can only be obtained from the argument of `thread_fn` in a new thread started by [`Thread::start`].
|
|
||||||
///
|
|
||||||
/// It will terminate current thread on drop.
|
|
||||||
///
|
|
||||||
/// [`Thread`]: crate::task::Thread
|
|
||||||
/// [`Thread::start`]: crate::task::Thread::start
|
|
||||||
pub struct CurrentThread(pub(super) Arc<Thread>);
|
|
||||||
|
|
||||||
impl Deref for CurrentThread {
|
|
||||||
type Target = Arc<Thread>;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for CurrentThread {
|
|
||||||
/// Terminate the current running thread.
|
|
||||||
fn drop(&mut self) {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
inner.change_state(ThreadState::Dead);
|
|
||||||
self.proc().remove_thread(self.base.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CurrentThread {
|
|
||||||
/// Exit the current thread.
|
|
||||||
///
|
|
||||||
/// The thread do not terminate immediately when exited. It is just made dying.
|
|
||||||
/// It will terminate after some cleanups on this struct drop.
|
|
||||||
pub fn exit(&self) {
|
|
||||||
self.stop(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wait until the thread is ready to run (not suspended),
|
|
||||||
/// and then take away its context to run the thread.
|
|
||||||
pub fn wait_for_run(&self) -> impl Future<Output = Box<UserContext>> {
|
|
||||||
#[must_use = "wait_for_run does nothing unless polled/`await`-ed"]
|
|
||||||
struct RunnableChecker {
|
|
||||||
thread: Arc<Thread>,
|
|
||||||
}
|
|
||||||
impl Future for RunnableChecker {
|
|
||||||
type Output = Box<UserContext>;
|
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
|
||||||
let mut inner = self.thread.inner.lock();
|
|
||||||
if inner.state() != ThreadState::Suspended {
|
|
||||||
// resume: return the context token from thread object
|
|
||||||
// There is no need to call change_state here
|
|
||||||
// since take away the context of a non-suspended thread won't change it's state
|
|
||||||
Poll::Ready(inner.context.take().unwrap())
|
|
||||||
} else {
|
|
||||||
// suspend: put waker into the thread object
|
|
||||||
inner.waker = Some(cx.waker().clone());
|
|
||||||
Poll::Pending
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RunnableChecker {
|
|
||||||
thread: self.0.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The thread ends running and takes back the context.
|
|
||||||
pub fn end_running(&self, context: Box<UserContext>) {
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
inner.context = Some(context);
|
|
||||||
// let state = inner.state;
|
|
||||||
// inner.change_state(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Access saved context of current thread.
|
|
||||||
///
|
|
||||||
/// Will panic if the context is not availiable.
|
|
||||||
pub fn with_context<T, F>(&self, f: F) -> T
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut UserContext) -> T,
|
|
||||||
{
|
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
let mut cx = inner.context.as_mut().unwrap();
|
|
||||||
f(&mut cx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The thread state.
|
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
|
||||||
pub enum ThreadState {
|
|
||||||
/// The thread has been created but it has not started running yet.
|
|
||||||
New = 0,
|
|
||||||
/// The thread is running user code normally.
|
|
||||||
Running = 1,
|
|
||||||
/// Stopped due to `zx_task_suspend()`.
|
|
||||||
Suspended = 2,
|
|
||||||
/// In a syscall or handling an exception.
|
|
||||||
Blocked = 3,
|
|
||||||
/// The thread is in the process of being terminated, but it has not been stopped yet.
|
|
||||||
Dying = 4,
|
|
||||||
/// The thread has stopped running.
|
|
||||||
Dead = 5,
|
|
||||||
/// The thread is stopped in an exception.
|
|
||||||
BlockedException = 0x103,
|
|
||||||
/// The thread is stopped in `zx_nanosleep()`.
|
|
||||||
BlockedSleeping = 0x203,
|
|
||||||
/// The thread is stopped in `zx_futex_wait()`.
|
|
||||||
BlockedFutex = 0x303,
|
|
||||||
/// The thread is stopped in `zx_port_wait()`.
|
|
||||||
BlockedPort = 0x403,
|
|
||||||
/// The thread is stopped in `zx_channel_call()`.
|
|
||||||
BlockedChannel = 0x503,
|
|
||||||
/// The thread is stopped in `zx_object_wait_one()`.
|
|
||||||
BlockedWaitOne = 0x603,
|
|
||||||
/// The thread is stopped in `zx_object_wait_many()`.
|
|
||||||
BlockedWaitMany = 0x703,
|
|
||||||
/// The thread is stopped in `zx_interrupt_wait()`.
|
|
||||||
BlockedInterrupt = 0x803,
|
|
||||||
/// Pager.
|
|
||||||
BlockedPager = 0x903,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for ThreadState {
|
|
||||||
fn default() -> Self {
|
|
||||||
ThreadState::New
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The thread information.
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct ThreadInfo {
|
|
||||||
state: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::job::Job;
|
|
||||||
use super::*;
|
|
||||||
use core::time::Duration;
|
|
||||||
use kernel_hal::timer_now;
|
|
||||||
use kernel_hal::GeneralRegs;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn create() {
|
|
||||||
let root_job = Job::root();
|
|
||||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
|
||||||
let thread = Thread::create(&proc, "thread").expect("failed to create thread");
|
|
||||||
assert_eq!(thread.flags(), ThreadFlag::empty());
|
|
||||||
|
|
||||||
assert_eq!(thread.related_koid(), proc.id());
|
|
||||||
let child = proc.get_child(thread.id()).unwrap().downcast_arc().unwrap();
|
|
||||||
assert!(Arc::ptr_eq(&child, &thread));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_std::test]
|
|
||||||
async fn start() {
|
|
||||||
kernel_hal_unix::init();
|
|
||||||
let root_job = Job::root();
|
|
||||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
|
||||||
let thread = Thread::create(&proc, "thread").expect("failed to create thread");
|
|
||||||
let thread1 = Thread::create(&proc, "thread1").expect("failed to create thread");
|
|
||||||
|
|
||||||
// function for new thread
|
|
||||||
async fn new_thread(thread: CurrentThread) {
|
|
||||||
let cx = thread.wait_for_run().await;
|
|
||||||
assert_eq!(cx.general.rip, 1);
|
|
||||||
assert_eq!(cx.general.rsp, 4);
|
|
||||||
assert_eq!(cx.general.rdi, 3);
|
|
||||||
assert_eq!(cx.general.rsi, 2);
|
|
||||||
async_std::task::sleep(Duration::from_millis(10)).await;
|
|
||||||
thread.end_running(cx);
|
|
||||||
}
|
|
||||||
|
|
||||||
// start a new thread
|
|
||||||
let handle = Handle::new(proc.clone(), Rights::DEFAULT_PROCESS);
|
|
||||||
proc.start(&thread, 1, 4, Some(handle.clone()), 2, |thread| {
|
|
||||||
Box::pin(new_thread(thread))
|
|
||||||
})
|
|
||||||
.expect("failed to start thread");
|
|
||||||
|
|
||||||
// check info and state
|
|
||||||
let info = proc.get_info();
|
|
||||||
assert!(info.started && !info.has_exited && info.return_code == 0);
|
|
||||||
assert_eq!(proc.status(), Status::Running);
|
|
||||||
assert_eq!(thread.state(), ThreadState::Running);
|
|
||||||
|
|
||||||
// start again should fail
|
|
||||||
assert_eq!(
|
|
||||||
proc.start(&thread, 1, 4, Some(handle.clone()), 2, |thread| Box::pin(
|
|
||||||
new_thread(thread)
|
|
||||||
)),
|
|
||||||
Err(ZxError::BAD_STATE)
|
|
||||||
);
|
|
||||||
|
|
||||||
// start another thread should fail
|
|
||||||
assert_eq!(
|
|
||||||
proc.start(&thread1, 1, 4, Some(handle.clone()), 2, |thread| Box::pin(
|
|
||||||
new_thread(thread)
|
|
||||||
)),
|
|
||||||
Err(ZxError::BAD_STATE)
|
|
||||||
);
|
|
||||||
|
|
||||||
// wait 100ms for the new thread to exit
|
|
||||||
async_std::task::sleep(core::time::Duration::from_millis(100)).await;
|
|
||||||
|
|
||||||
// no other references to `Thread`
|
|
||||||
assert_eq!(Arc::strong_count(&thread), 1);
|
|
||||||
assert_eq!(thread.state(), ThreadState::Dead);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn info() {
|
|
||||||
let root_job = Job::root();
|
|
||||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
|
||||||
let thread = Thread::create(&proc, "thread").expect("failed to create thread");
|
|
||||||
|
|
||||||
let info = thread.get_thread_info();
|
|
||||||
assert!(info.state == thread.state() as u32);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_write_state() {
|
|
||||||
let root_job = Job::root();
|
|
||||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
|
||||||
let thread = Thread::create(&proc, "thread").expect("failed to create thread");
|
|
||||||
|
|
||||||
const SIZE: usize = core::mem::size_of::<GeneralRegs>();
|
|
||||||
let mut buf = [0; 10];
|
|
||||||
assert_eq!(
|
|
||||||
thread.read_state(ThreadStateKind::General, &mut buf).err(),
|
|
||||||
Some(ZxError::BAD_STATE)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
thread.write_state(ThreadStateKind::General, &buf).err(),
|
|
||||||
Some(ZxError::BAD_STATE)
|
|
||||||
);
|
|
||||||
|
|
||||||
thread.suspend();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
thread.read_state(ThreadStateKind::General, &mut buf).err(),
|
|
||||||
Some(ZxError::BUFFER_TOO_SMALL)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
thread.write_state(ThreadStateKind::General, &buf).err(),
|
|
||||||
Some(ZxError::BUFFER_TOO_SMALL)
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut buf = [0; SIZE];
|
|
||||||
assert!(thread
|
|
||||||
.read_state(ThreadStateKind::General, &mut buf)
|
|
||||||
.is_ok());
|
|
||||||
assert!(thread.write_state(ThreadStateKind::General, &buf).is_ok());
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_std::test]
|
|
||||||
async fn wait_for_run() {
|
|
||||||
let root_job = Job::root();
|
|
||||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
|
||||||
let thread = Thread::create(&proc, "thread").expect("failed to create thread");
|
|
||||||
|
|
||||||
assert_eq!(thread.state(), ThreadState::New);
|
|
||||||
|
|
||||||
thread
|
|
||||||
.start(0, 0, 0, 0, |thread| Box::pin(new_thread(thread)))
|
|
||||||
.unwrap();
|
|
||||||
async fn new_thread(thread: CurrentThread) {
|
|
||||||
assert_eq!(thread.state(), ThreadState::Running);
|
|
||||||
|
|
||||||
// without suspend
|
|
||||||
let context = thread.wait_for_run().await;
|
|
||||||
thread.end_running(context);
|
|
||||||
|
|
||||||
// with suspend
|
|
||||||
thread.suspend();
|
|
||||||
thread.suspend();
|
|
||||||
assert_eq!(thread.state(), ThreadState::Suspended);
|
|
||||||
async_std::task::spawn({
|
|
||||||
let thread = (*thread).clone();
|
|
||||||
async move {
|
|
||||||
async_std::task::sleep(Duration::from_millis(10)).await;
|
|
||||||
thread.resume();
|
|
||||||
async_std::task::sleep(Duration::from_millis(10)).await;
|
|
||||||
thread.resume();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let time = timer_now();
|
|
||||||
let _context = thread.wait_for_run().await;
|
|
||||||
assert!(timer_now() - time >= Duration::from_millis(20));
|
|
||||||
}
|
|
||||||
// FIX ME
|
|
||||||
// let thread: Arc<dyn KernelObject> = thread;
|
|
||||||
// thread.wait_signal(Signal::THREAD_TERMINATED).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn time() {
|
|
||||||
let root_job = Job::root();
|
|
||||||
let proc = Process::create(&root_job, "proc").expect("failed to create process");
|
|
||||||
let thread = Thread::create(&proc, "thread").expect("failed to create thread");
|
|
||||||
|
|
||||||
assert_eq!(thread.get_time(), 0);
|
|
||||||
thread.time_add(10);
|
|
||||||
assert_eq!(thread.get_time(), 10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,64 +0,0 @@
|
|||||||
use crate::{ZxError, ZxResult};
|
|
||||||
use kernel_hal::UserContext;
|
|
||||||
use numeric_enum_macro::numeric_enum;
|
|
||||||
|
|
||||||
numeric_enum! {
|
|
||||||
#[repr(u32)]
|
|
||||||
/// Possible values for "kind" in zx_thread_read_state and zx_thread_write_state.
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub enum ThreadStateKind {
|
|
||||||
General = 0,
|
|
||||||
FS = 6,
|
|
||||||
GS = 7,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) trait ContextExt {
|
|
||||||
fn read_state(&self, kind: ThreadStateKind, buf: &mut [u8]) -> ZxResult<usize>;
|
|
||||||
fn write_state(&mut self, kind: ThreadStateKind, buf: &[u8]) -> ZxResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ContextExt for UserContext {
|
|
||||||
fn read_state(&self, kind: ThreadStateKind, buf: &mut [u8]) -> ZxResult<usize> {
|
|
||||||
match kind {
|
|
||||||
ThreadStateKind::General => buf.write_struct(&self.general),
|
|
||||||
ThreadStateKind::FS => buf.write_struct(&self.general.fsbase),
|
|
||||||
ThreadStateKind::GS => buf.write_struct(&self.general.gsbase),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_state(&mut self, kind: ThreadStateKind, buf: &[u8]) -> ZxResult {
|
|
||||||
match kind {
|
|
||||||
ThreadStateKind::General => self.general = buf.read_struct()?,
|
|
||||||
ThreadStateKind::FS => self.general.fsbase = buf.read_struct()?,
|
|
||||||
ThreadStateKind::GS => self.general.gsbase = buf.read_struct()?,
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
trait BufExt {
|
|
||||||
fn read_struct<T>(&self) -> ZxResult<T>;
|
|
||||||
fn write_struct<T: Copy>(&mut self, value: &T) -> ZxResult<usize>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
impl BufExt for [u8] {
|
|
||||||
fn read_struct<T>(&self) -> ZxResult<T> {
|
|
||||||
if self.len() < core::mem::size_of::<T>() {
|
|
||||||
return Err(ZxError::BUFFER_TOO_SMALL);
|
|
||||||
}
|
|
||||||
Ok(unsafe { (self.as_ptr() as *const T).read() })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_struct<T: Copy>(&mut self, value: &T) -> ZxResult<usize> {
|
|
||||||
if self.len() < core::mem::size_of::<T>() {
|
|
||||||
return Err(ZxError::BUFFER_TOO_SMALL);
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
*(self.as_mut_ptr() as *mut T) = *value;
|
|
||||||
}
|
|
||||||
Ok(core::mem::size_of::<T>())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
//! Objects for Virtual Memory Management.
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
mod vmar;
|
|
||||||
|
|
||||||
pub use self::vmar::*;
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue