Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce libgreen/libnative, the M:N and 1:1 scheduling libraries #10965

Merged
merged 38 commits into from
Dec 26, 2013

Conversation

alexcrichton
Copy link
Member

This pull request extracts all scheduling functionality from libstd, moving it into its own separate crates. The new libnative and libgreen will be the new way in which 1:1 and M:N scheduling is implemented. The standard library still requires an interface to the runtime, however, (think of things like std::comm and io::println). The interface is now defined by the Runtime trait inside of std::rt.

The booting process is now that libgreen defines the start lang-item and that's it. I want to extend this soon to have libnative also have a "start lang item" but also allow libgreen and libnative to be linked together in the same process. For now though, only libgreen can be used to start a program (unless you define the start lang item yourself). Again though, I want to change this soon, I just figured that this pull request is large enough as-is.

This certainly wasn't a smooth transition, certain functionality has no equivalent in this new separation, and some functionality is now better enabled through this new system. I did my best to separate all of the commits by topic and keep things fairly bite-sized, although are indeed larger than others.

As a note, this is currently rebased on top of my std::comm rewrite (or at least an old copy of it), but none of those commits need reviewing (that will all happen in another pull request).

@pnkfelix
Copy link
Member

cc me (i'm no expert in this area but I am fascinated/hypnotized by the design space here)

@liigo
Copy link
Contributor

liigo commented Dec 14, 2013

Great work! Thanks
在 2013年12月14日 下午4:19,"Alex Crichton" [email protected]写道:

This pull request extracts all scheduling functionality from libstd,
moving it into its own separate crates. The new libnative and libgreen will
be the new way in which 1:1 and M:N scheduling is implemented. The standard
library still requires an interface to the runtime, however, (think of
things like std::comm and io::println). The interface is now defined by
the Runtime trait inside of std::rt.

The booting process is now that libgreen defines the start lang-item and
that's it. I want to extend this soon to have libnative also have a "start
lang item" but also allow libgreen and libnative to be linked together in
the same process. For now though, only libgreen can be used to start a
program (unless you define the start lang item yourself). Again though, I
want to change this soon, I just figured that this pull request is large
enough as-is.

This certainly wasn't a smooth transition, certain functionality has no
equivalent in this new separation, and some functionality is now better
enabled through this new system. I did my best to separate all of the
commits by topic and keep things fairly bite-sized, although are indeed
larger than others.

As a note, this is currently rebased on top of my std::comm rewrite (or
at least an old copy of it), but none of those commits need reviewing (that

will all happen in another pull request).

You can merge this Pull Request by running

git pull https://github.com/alexcrichton/rust libgreen

Or view, comment on, or merge it at:

#10965
Commit Summary

  • Rewrite std::comm
  • Fallout of rewriting std::comm
  • Fixes from review I'll rebase later
  • Fix select, will rebase into first commit later
  • std: Introduce a Runtime trait
  • std: Introduce an unstable::stack module
  • std: Delete rt::test
  • std: Introduce std::sync
  • std: Expose that LocalIo may not always be available
  • std: Handle prints with literally no context
  • std: Make logging safely implemented
  • std: Change Any::move to never consume the object
  • std: Remove std::select
  • rustuv: Reimplement without using std::rt::sched
  • std: Move management of the exit code to std::os
  • std: Reimplement std::comm without the scheduler
  • native: Introduce libnative
  • extra: Make a few extra::comm methods public
  • green: Rip the bandaid off, introduce libgreen
  • std: Fix a bug where Local::take() didn't zero out
  • std: Update std::rt::thread to specify stack sizes
  • make: Add all the make support for lib{native,green}
  • std: Get stdtest all passing again
  • native: Add tests and cleanup entry points
  • rustuv: Get all tests passing again after refactor
  • Finalize the green::Pool type
  • rustuv: Write homing tests with SchedPool
  • std: Remove deferred sending functions from std::comm
  • std: Implement yields on receives for channels
  • green: Allow specifying an IoFactory for pools
  • green: Fixing all tests from previous refactorings
  • rustc: Temporarily inject libgreen with librustuv
  • Test fixes and rebase problems

File Changes

Patch Links:

@emberian
Copy link
Member

And yet still manage to have a negative diffstat.

This is an exciting PR. I reviewed the first half of it and looked over the second half. I think Runtime basically makes sense.

Moving forward, how do you see mixed-native-and-green programs working, and what interface can we expose to C to embed runtimed Rust code?

@alexcrichton
Copy link
Member Author

Mixing the two runtimes will be done by linking to libgreen and libnative manually, and it would look something like this:

extern mod native;
extern mod green;

fn spawn_native() {
    do native::task::spawn(TaskOpts::new()) {
        // this is forced to be a 1:1 task (this is a new OS thread)
    }
}

fn spawn_green_pool() {
    let mut pool = green::SchedPool::new(green::SchedConfig::new());
    do pool.spawn(TaskOpts::new()) {
        // this is an M:N task running in the above pool
        do spawn {
            // this is also an M:N task running in the above pool
        }
    }
    pool.shutdown();
}

The interface that I want to expose to C hasn't quite been worked out just yet. The interface would look someting like:

fn some_function_called_from_c() {
    let t: ~Task = acquire_the_c_task();
    let result: Result<~Task, ~Any> = t.run(|| { /* rust runtime activated, all services available (including unwinding) */ });
    deal_with_result(result)
}

The idea here is that C code interfacing with rust code requiring the runtime would need to pass around and manage a ~Task object. The task should not be allocated every time rust is called into, but rather preserved across function calls. The task then has a function fn run(~self, closure: ||) -> Result<~Task, ~Any> which executes the closure in the context of the Task, and once the closure finishes the task is returned back (on success), or ~Any is returned (on task failure). This boundary is intended to be "fairly cheap" in that right now it's just a C++ try/catch block which is much more lightweight than spawning a new task.

I don't have the C interface implemented just yet (I figured this pull request was large enough), but I plan on doing it soon after landing this.

@emberian
Copy link
Member

LGTM! Thanks.

@brson
Copy link
Contributor

brson commented Dec 18, 2013

After this PR what is the solution for people using the old rt::start_on_main_thread function?

@alexcrichton
Copy link
Member Author

I was under the impression that the users of rt::start_on_main_thread were not using the scheduler on the main thread but were rather just pinning a task to it. In that case, I would expect the main thread to be a native task which manages the green scheduler pool (and the scheduler pool has channels coming back out to the main native task possibly. Does that make sense? We could also have a method which consumes the pool and spawns a scheduler on the invoking thread, returning once all threads in the pool have exited.

@alexcrichton
Copy link
Member Author

@brson, I have pushed with many updates to the end (all the old commits are just rebased versions of their former selves)

@alexcrichton
Copy link
Member Author

Still not ready for merging yet though as I have no replacement for run_on_main_thread just yet.

@alexcrichton
Copy link
Member Author

After rethinking, I believe that this is now ready for merging. I have removed the awoken: AtomicBool in favor of awoken: bool, and I believe that start_on_main_thread can be sufficiently well emulated via

extern mod native;
extern mod green;

use std::task::TaskOpts;

#[start]
fn start(argc: int, argv: **u8) -> int {
    do native::start(argc, argv) {
        let mut pool = green::SchedPool::new(green::PoolConfig::new());
        let (p, c) = Chan::new();
        do pool.spawn(TaskOpts::new()) {
            println!("green pool");
            c.send(());
        }
        p.recv();
        pool.shutdown();
    }
}

I found out that native::lang_start is the one that spawns a new thread to run the main task, but native::start invokes the procedure on the main thread, so this is exactly what gui applications would want.

All that being said, this could use some help with documentation, but I'd like to sort out #[boot] and friends before writing something up. This is going into the realm of a separate tutorial, but I can definitely write something up soon.

@brson
Copy link
Contributor

brson commented Dec 24, 2013

How does native::start set up the stack baundary?

r=me

@alexcrichton
Copy link
Member Author

Right now native::start isn't actually responsible for setting up the stack boundary (because it has no way of knowing it), but rather the stack boundary is set up in native::lang_start which spawns a new task (which knows about the stack boundary).

I'll open an issue about this.

This trait is used to abstract the differences between 1:1 and M:N scheduling
and is the sole dispatch point for the differences between these two scheduling
modes.

This, and the following series of commits, is not intended to compile. Only
after the entire transition is complete are programs expected to compile.
This module will be used to manage the OS-specific TLS registers used to specify
the bounds of the current rust stack (useful in 1:1 and M:N)
This module contains many M:N specific concepts. This will no longer be
available with libgreen, and most functions aren't really that necessary today
anyway. New testing primitives will be introduced as they become available for
1:1 and M:N.

A new io::test module is introduced with the new ip4/ip6 address helpers to
continue usage in io tests.
For now, this moves the following modules to std::sync

* UnsafeArc (also removed unwrap method)
* mpsc_queue
* spsc_queue
* atomics
* mpmc_bounded_queue
* deque

We may want to remove some of the queues, but for now this moves things out of
std::rt into std::sync
It is not the case that all programs will always be able to acquire an instance
of the LocalIo borrow, so this commit exposes this limitation by returning
Option<LocalIo> from LocalIo::borrow().

At the same time, a helper method LocalIo::maybe_raise() has been added in order
to encapsulate the functionality of raising on io_error if there is on local I/O
available.
Printing is an incredibly useful debugging utility, and it's not much help if
your debugging prints just trigger an obscure abort when you need them most. In
order to handle this case, forcibly fall back to a libc::write implementation of
printing whenever a local task is not available.

Note that this is *not* a 1:1 fallback. All 1:1 rust tasks will still have a
local Task that it can go through (and stdio will be created through the local
IO factory), this is only a fallback for "no context" rust code (such as that
setting up the context).
This commit fixes the logging function to be safely implemented, as well as
forcibly requiring a task to be present to use logging macros. This is safely
implemented by transferring ownership of the logger from the task to the local
stack frame in order to perform the print. This means that if a logger does more
logging while logging a new one will be initialized and then will get
overwritten once the initial logging function returns.

Without a scheme such as this, it is possible to unsafely alias two loggers by
logging twice (unsafely borrows from the task twice).
This allows creation of different sched pools with different io factories.
Namely, this will be used to test the basic I/O loop in the green crate. This
can also be used to override the global default.
This measure is simply to allow programs to continue compiling as they once did.
In the future, this needs a more robust solution to choose how to start with
libgreen or libnative.
Note that this removes a number of run-pass tests which are exercising behavior
of the old runtime. This functionality no longer exists and is thoroughly tested
inside of libgreen and libnative. There isn't really the notion of "starting the
runtime" any more. The major notion now is "bootstrapping the initial task".
There was a race in the code previously where schedulers would *immediately*
shut down after spawning the main task (because the global task count would
still be 0). This fixes the logic by blocking the sched pool task in receving on
a port instead of spawning a task into the pool to receive on a port.

The modifications necessary were to have a "simple task" running by the time the
code is executing, but this is a simple enough thing to implement and I forsee
this being necessary to have implemented in the future anyway.
The queue has an async handle which must be destroyed before the loop is
destroyed, so use a little bit of an option dance to get around this
requirement.
It doesn't actually and we can get better incremental build times for
modifications to librustuv if libsyntax/librustc don't need to get rebuilt
This is a very real problem with cvars on normal systems, and all of channels
will not work if spurious wakeups are accepted. This problem is just solved with
a synchronized flag (accessed in the cvar's lock) to see whether a signal()
actually happened or whether it's spurious.
The uv loop was being destroyed before the async handle was being destroyed, so
closing the async handle was causing a use-after-free in the uv loop. This was
fixed by moving destruction of the queue's async handle to an earlier location
and then actually freeing it once the loop has been dropped.
This happened because the environment of a procedure was never deallocated.
The rmake tests should depend on the target libraries (for linking), not just
the host libraries (for running). The host file dependencies are also correct
now because HLIBRUSTC_DEFAULT doesn't actually exist.
This prevents usage of the win32 utf-16 helper functions from outside of libstd.

Closes rust-lang#9053
The only user of this was the homing code in librustuv, and it just manually
does the cast from a pointer to a uint now.
These functions are all unnecessary now, and they only have meaning in the M:N
context. Removing these functions uncovered a bug in the librustuv timer
bindings, but it was fairly easy to cover (and the test is already committed).

These cannot be completely removed just yet due to their usage in the WaitQueue
of extra::sync, and until the mutex in libextra is rewritten it will not be
possible to remove the deferred sends for channels.
This test also had a race condition in using the cvar/lock, so I fixed that up
as well. The race originated from one half trying to destroy the lock when
another half was using it.
* vec::raw::to_ptr is gone
* Pausible => Pausable
* Removing @
* Calling the main task "<main>"
* Removing unused imports
* Removing unused mut
* Bringing some libextra tests up to date
* Allowing compiletest to work at stage0
* Fixing the bootstrap-from-c rmake tests
* assert => rtassert in a few cases
* printing to stderr instead of stdout in fail!()
bors added a commit that referenced this pull request Dec 26, 2013
This pull request extracts all scheduling functionality from libstd, moving it into its own separate crates. The new libnative and libgreen will be the new way in which 1:1 and M:N scheduling is implemented. The standard library still requires an interface to the runtime, however, (think of things like `std::comm` and `io::println`). The interface is now defined by the `Runtime` trait inside of `std::rt`.

The booting process is now that libgreen defines the start lang-item and that's it. I want to extend this soon to have libnative also have a "start lang item" but also allow libgreen and libnative to be linked together in the same process. For now though, only libgreen can be used to start a program (unless you define the start lang item yourself). Again though, I want to change this soon, I just figured that this pull request is large enough as-is.

This certainly wasn't a smooth transition, certain functionality has no equivalent in this new separation, and some functionality is now better enabled through this new system. I did my best to separate all of the commits by topic and keep things fairly bite-sized, although are indeed larger than others.

As a note, this is currently rebased on top of my `std::comm` rewrite (or at least an old copy of it), but none of those commits need reviewing (that will all happen in another pull request).
@bors bors closed this Dec 26, 2013
@bors bors merged commit 6cad8f4 into rust-lang:master Dec 26, 2013
@alexcrichton alexcrichton deleted the libgreen branch December 27, 2013 05:28
reem added a commit to reem/rust that referenced this pull request Aug 8, 2014
After rust-lang#10965, which introduces no-context printing, this is no longer needed.

Fixes rust-lang#11043
@reem reem mentioned this pull request Aug 8, 2014
flip1995 pushed a commit to flip1995/rust that referenced this pull request Jun 30, 2023
…endoo

Make `--explain` subcommand return 1 for missing lints

changelog: The `--explain` subcommand now exits with the 1 exit code for missing lints
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants