mirror of
https://github.com/boostorg/fiber.git
synced 2026-02-11 23:52:29 +00:00
Merge pull request #48 from nat-goodspeed/customization
Flesh out Customization section with step-by-step directions.
This commit is contained in:
@@ -266,16 +266,15 @@ time as (system time + `timeout_duration`).
|
||||
bounded_channel( std::size_t hwm, std::size_t lwm);
|
||||
|
||||
[variablelist
|
||||
[[Preconditions:] [`hwm >= lwm`]]
|
||||
[[Preconditions:] [`hwm > lwm`]]
|
||||
[[Effects:] [Constructs an object of class `bounded_channel`. The constructor
|
||||
with two arguments constructs an object of class `bounded_channel` with a
|
||||
high-watermark of `hwm` and a low-watermark of `lwm` items. The constructor
|
||||
with one argument effectively sets both `hwm` and `lwm` to the same value
|
||||
`wm`.]]
|
||||
[[Throws:] [`invalid_argument` if `lwm > hwm`.]]
|
||||
with one argument is effectively the same as `bounded_channel(wm, (wm-1))`.]]
|
||||
[[Throws:] [`invalid_argument` if `lwm >= hwm`.]]
|
||||
[[Notes:] [Once the number of values in the channel reaches `hwm`, any call to
|
||||
`push()`, `push_wait_for()` or `push_wait_until()` will block until the number
|
||||
of values in the channel has dropped below `lwm`. That is, if `lwm < hwm`, the
|
||||
of values in the channel is at most `lwm`. That is, if `lwm < (hwm-1)`, the
|
||||
channel can be in a state in which `push()`, `push_wait_for()` or `push_wait_until()`
|
||||
calls will block (channel is full) even though the number of values
|
||||
in the channel is less than `hwm`.]]
|
||||
@@ -303,7 +302,7 @@ in the channel is less than `hwm`.]]
|
||||
|
||||
[template bounded_channel_push_effects[or] [xchannel_push_effects If channel
|
||||
is not full, enqueues] Otherwise the calling fiber is suspended until
|
||||
the number of values in the channel drops below `lwm` (return value
|
||||
the number of values in the channel drops to `lwm` (return value
|
||||
`success`)[or] the channel is `close()`d (return value `closed`)]
|
||||
|
||||
[member_heading bounded_channel..push]
|
||||
@@ -361,7 +360,7 @@ time_point (return value `timeout`).]]
|
||||
]
|
||||
|
||||
[template bounded_pop_unblocking[] Once the number of items remaining in the
|
||||
channel drops below `lwm`, any fibers blocked on `push()`, `push_wait_for()`
|
||||
channel drops to `lwm`, any fibers blocked on `push()`, `push_wait_for()`
|
||||
or `push_wait_until()` may resume.]
|
||||
|
||||
[xchannel_pop bounded_channel... [bounded_pop_unblocking]]
|
||||
|
||||
@@ -5,24 +5,125 @@
|
||||
http://www.boost.org/LICENSE_1_0.txt
|
||||
]
|
||||
|
||||
[import ../examples/priority.cpp]
|
||||
|
||||
[#custom]
|
||||
[section:custom Customization]
|
||||
|
||||
__boost_fiber__ allows to customize the scheduling algorithm by a user-defined
|
||||
implementation.
|
||||
A fiber-scheduler must implement interface __algo__. __boost_fiber__ provides
|
||||
scheduler [class_link round_robin].
|
||||
[heading Overview]
|
||||
|
||||
void thread_fn() {
|
||||
my_fiber_scheduler mfs;
|
||||
boost::fibers::set_scheduling_algorithm( & mfs);
|
||||
...
|
||||
}
|
||||
As noted in the [link scheduling Scheduling] section, by default
|
||||
__boost_fiber__ uses its own [class_link round_robin] scheduler for each
|
||||
thread. To control the way __boost_fiber__ schedules ready fibers on a
|
||||
particular thread, in general you must follow several steps. This section
|
||||
discusses those steps, whereas [link scheduling Scheduling] serves as a
|
||||
reference for the classes involved.
|
||||
|
||||
[heading fiber_context]
|
||||
The library's fiber manager keeps track of suspended (blocked) fibers. Only
|
||||
when a fiber becomes ready to run is it passed to the scheduler. Of course, if
|
||||
there are fewer than two ready fibers, the scheduler's job is trivial. Only
|
||||
when there are two or more ready fibers does the particular scheduler
|
||||
implementation start to influence the overall sequence of fiber execution.
|
||||
|
||||
The fiber_context class is mostly supposed to be opaque. However, certain
|
||||
members are available for use by a custom scheduling algorithm.
|
||||
In this section we illustrate a simple custom scheduler that honors an integer
|
||||
fiber priority. We will implement it such that a fiber with higher priority is
|
||||
preferred over a fiber with lower priority. Any fibers with equal priority
|
||||
values are serviced on a round-robin basis.
|
||||
|
||||
The full source code for the examples below is found in
|
||||
[@boost:/libs/fiber/examples/priority.cpp priority.cpp].
|
||||
|
||||
[heading Custom Property Class]
|
||||
|
||||
The first essential point is that we must associate an integer priority with
|
||||
each fiber.[footnote A previous version of the Fiber library implicitly
|
||||
tracked an int priority for each fiber, even though the default scheduler
|
||||
ignored it. This has been dropped, since the library now supports arbitrary
|
||||
scheduler-specific fiber properties.]
|
||||
|
||||
One might suggest deriving a custom [class_link fiber] subclass to store such
|
||||
properties. There are a couple of reasons for the present mechanism.
|
||||
|
||||
# __boost_fiber__ provides a number of different ways to launch a fiber.
|
||||
(Consider [ns_function_link fibers..async].) Higher-level libraries might
|
||||
introduce additional such wrapper functions. A custom scheduler must
|
||||
associate its custom properties with ['every] fiber in the thread, not only
|
||||
the ones explicitly launched by instantiating a custom `fiber` subclass.
|
||||
# Consider a large existing program that launches fibers in many different
|
||||
places in the code. We discover a need to introduce a custom scheduler for a
|
||||
particular thread. If supporting that scheduler's custom properties required
|
||||
a particular `fiber` subclass, we would have to hunt down and modify every
|
||||
place that launches a fiber on that thread.
|
||||
|
||||
The present mechanism allows you to "drop in" a custom scheduler with its
|
||||
attendant custom properties ['without] altering the rest of your application.
|
||||
|
||||
Instead of deriving a custom scheduler fiber properties subclass from
|
||||
[class_link fiber], you must instead derive it from [class_link
|
||||
fiber_properties].
|
||||
|
||||
[priority_props]
|
||||
|
||||
[heading Custom Scheduler Class]
|
||||
|
||||
Now we can derive a custom scheduler from [template_link
|
||||
sched_algorithm_with_properties], specifying our custom property class
|
||||
`priority_props` as the template parameter.
|
||||
|
||||
Your custom scheduler can track ready [^[class_link fiber_context]*]s using
|
||||
whatever container tactic you prefer. In this example, we use
|
||||
[data_member_link fiber_context..nxt] to hand-implement an intrusive
|
||||
singly-linked list.
|
||||
|
||||
[priority_scheduler]
|
||||
|
||||
Our example `priority_scheduler` doesn't override [member_link
|
||||
sched_algorithm_with_properties..new_properties]: we're content with
|
||||
allocating `priority_props` instances on the heap.
|
||||
|
||||
[heading Replace Default Scheduler]
|
||||
|
||||
You must call [function_link set_scheduling_algorithm] at the start of each
|
||||
thread on which you want __boost_fiber__ to use your custom scheduler rather
|
||||
than its own default [class_link round_robin]. Specifically, you must call
|
||||
`set_scheduling_algorithm()` before performing any other __boost_fiber__
|
||||
operations on that thread.
|
||||
|
||||
It works to instantiate your custom [template_link
|
||||
sched_algorithm_with_properties] subclass on the stack at the start of your
|
||||
thread function, passing the instance pointer to `set_scheduling_algorithm()`.
|
||||
This ensures that your scheduler instance will live as long as the thread
|
||||
itself. (Of course `main()` is, in effect, the thread function for the
|
||||
application's main thread.)
|
||||
|
||||
[main]
|
||||
|
||||
[heading Use Properties]
|
||||
|
||||
The running fiber can access its own [class_link fiber_properties] subclass
|
||||
instance by calling [ns_function_link this_fiber..properties]. Although
|
||||
`properties<>()` is a nullary function, you must pass, as a template
|
||||
parameter, the `fiber_properties` subclass.
|
||||
|
||||
[init]
|
||||
|
||||
Given a [class_link fiber] instance still connected with a running fiber (that
|
||||
is, not [member_link fiber..detach]ed), you may access that fiber's properties
|
||||
using [template_member_link fiber..properties]. As with
|
||||
`this_fiber::properties<>()`, you must pass your `fiber_properties` subclass
|
||||
as the template parameter.
|
||||
|
||||
[change_fn]
|
||||
|
||||
Since launching a new fiber schedules that fiber right away, code such as the
|
||||
following:
|
||||
|
||||
boost::fibers::fiber newfiber(fiber_function);
|
||||
newfiber.properties<priority_props>().name = "newfiber";
|
||||
|
||||
will not necessarily set the property as soon as you expect. It is generally
|
||||
preferable to pass the initial property values to your `fiber_function()` and
|
||||
have it set them itself. In the example above, `change_fn()` accepts its own
|
||||
`name` and `priority` and calls `init()` to set the corresponding properties.
|
||||
|
||||
[endsect]
|
||||
|
||||
@@ -440,7 +440,7 @@ template parameter than `PROPS`.]]
|
||||
user-coded scheduler to associate extended properties, such as priority, with
|
||||
a fiber instance. This method allows access to those user-provided properties
|
||||
-- but only after this fiber has been scheduled for the first time.]]
|
||||
[[See also:] [[link custom]]]
|
||||
[[See also:] [[link custom Customization]]]
|
||||
]
|
||||
|
||||
[member_heading fiber..operator bool]
|
||||
@@ -507,7 +507,7 @@ lifespan of the referenced `scheduler` object. The caller must eventually
|
||||
destroy the passed `scheduler`, just as it must allocate it in the first
|
||||
place.]]
|
||||
[[Throws:] [Nothing]]
|
||||
[[See also:] [[link scheduling], [link custom]]]
|
||||
[[See also:] [[link scheduling Scheduling], [link custom Customization]]]
|
||||
]
|
||||
|
||||
[function_heading ready_fibers]
|
||||
@@ -515,7 +515,7 @@ place.]]
|
||||
std::size_t ready_fibers();
|
||||
|
||||
[variablelist
|
||||
[[Returns:] [Returns the amount of fibers ready to run.]]
|
||||
[[Returns:] [Returns the number of fibers ready to run.]]
|
||||
[[Throws:] [Nothing]]
|
||||
]
|
||||
|
||||
@@ -531,9 +531,17 @@ place.]]
|
||||
fibers are ready to run.]]
|
||||
[[Throws:] [Nothing]]
|
||||
[[Notes:] [This setting can be used to adjust the "niceness" of an idle
|
||||
thread: a thread whose fibers are all currently blocked for various reasons. A
|
||||
longer `wait_interval()` causes an idle thread to use less CPU. The default
|
||||
for each thread, if not specified, is `std::chrono::milliseconds(10)`.]]
|
||||
thread: a thread whose fibers are all currently blocked for various reasons.
|
||||
The default for each thread, if not specified, is
|
||||
`std::chrono::milliseconds(10)`. A longer `wait_interval()` causes an idle
|
||||
thread to use less CPU. A shorter `wait_interval()` is more responsive to
|
||||
fibers becoming ready. When a fiber becomes ready to resume, e.g. a mutex on
|
||||
which it is blocked becomes available, control is not immediately passed to
|
||||
that fiber; instead it is marked "ready to run." It won't actually resume
|
||||
until the fiber manager wakes up to consult the scheduler. It could take as
|
||||
long as `wait_interval()` before the fiber manager notices the fiber's
|
||||
readiness. It is likely that the optimal setting for `wait_interval()` is
|
||||
application-specific.]]
|
||||
]
|
||||
|
||||
|
||||
@@ -747,7 +755,7 @@ template parameter than `PROPS`.]]
|
||||
user-coded scheduler to associate extended properties, such as priority, with
|
||||
a fiber instance. This function allows access to those user-provided
|
||||
properties.]]
|
||||
[[See also:] [[link custom]]]
|
||||
[[See also:] [[link custom Customization]]]
|
||||
]
|
||||
|
||||
[ns_function_heading this_fiber..interruption_point]
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
[template data_member_heading[class_name member_name]
|
||||
[hding [class_name]_[member_name]..Data member [`[member_name]]]
|
||||
]
|
||||
[template data_member_link[class_name member_name] [member_link [class_name]..[member_name]]]
|
||||
[template data_member_link[class_name member_name] [dblink [class_name]_[member_name]..[`[class_name]::[member_name]]]]
|
||||
|
||||
[template function_heading[function_name]
|
||||
[hding [function_name]..Non-member function [`[function_name]()]]
|
||||
|
||||
@@ -15,7 +15,7 @@ manager. Each time a fiber suspends (or yields), the fiber manager consults a
|
||||
scheduler to determine which fiber will run next.
|
||||
|
||||
__boost_fiber__ provides the fiber manager, but the scheduler is a
|
||||
customization point. (See [link custom].)
|
||||
customization point. (See [link custom Customization].)
|
||||
|
||||
Each thread has its own scheduler. By default, __boost_fiber__ implicitly
|
||||
instantiates [class_link round_robin] as the scheduler for each thread.
|
||||
@@ -83,8 +83,7 @@ queue.]]
|
||||
virtual std::size_t ready_fibers() const noexcept = 0;
|
||||
|
||||
[variablelist
|
||||
[[Effects:] [Returns 0 if scheduling algorithm has no fibers ready to run,
|
||||
otherwise nonzero.]]
|
||||
[[Effects:] [Returns the number of fibers ready to run.]]
|
||||
]
|
||||
|
||||
|
||||
@@ -126,8 +125,7 @@ of that queue, shares the thread between ready fibers in round-robin fashion.]]
|
||||
virtual std::size_t ready_fibers() const noexcept;
|
||||
|
||||
[variablelist
|
||||
[[Returns:] [0 if scheduling algorithm has no fibers ready to run, otherwise
|
||||
nonzero.]]
|
||||
[[Returns:] [Returns the number of fibers ready to run.]]
|
||||
]
|
||||
|
||||
|
||||
@@ -150,7 +148,7 @@ A custom fiber properties class must be derived from `fiber_properties`.
|
||||
|
||||
class fiber_properties {
|
||||
public:
|
||||
fiber_properties( back_ptr f);
|
||||
fiber_properties( fiber_context* f);
|
||||
|
||||
virtual ~fiber_properties() {}
|
||||
|
||||
@@ -160,12 +158,12 @@ A custom fiber properties class must be derived from `fiber_properties`.
|
||||
|
||||
[heading Constructor]
|
||||
|
||||
fiber_properties( back_ptr f);
|
||||
fiber_properties( fiber_context* f);
|
||||
|
||||
[variablelist
|
||||
[[Effects:] [Constructs base-class component of custom subclass.]]
|
||||
[[Note:] [Your subclass constructor must accept a `back_ptr` and pass it to
|
||||
the base-class `fiber_properties` constructor.]]
|
||||
[[Note:] [Your subclass constructor must accept a `fiber_context*` and pass it
|
||||
to the base-class `fiber_properties` constructor.]]
|
||||
]
|
||||
|
||||
[member_heading fiber_properties..notify]
|
||||
|
||||
@@ -8,19 +8,28 @@
|
||||
#include <boost/ref.hpp>
|
||||
#include <iostream>
|
||||
|
||||
//[priority_props
|
||||
class priority_props: public boost::fibers::fiber_properties
|
||||
{
|
||||
public:
|
||||
priority_props(boost::fibers::fiber_context* p):
|
||||
fiber_properties(p),
|
||||
fiber_properties(p), /*< Your subclass constructor must accept a
|
||||
[^[class_link fiber_context]*] and pass it to
|
||||
the `fiber_properties` constructor. >*/
|
||||
priority_(0)
|
||||
{}
|
||||
|
||||
int get_priority() const { return priority_; }
|
||||
int get_priority() const { return priority_; } /*< Provide read access
|
||||
methods at your own
|
||||
discretion. >*/
|
||||
|
||||
// Call this method to alter priority, because we must notify
|
||||
// priority_scheduler of any change.
|
||||
void set_priority(int p)
|
||||
void set_priority(int p) /*< It's important to call notify() on any
|
||||
change in a property that can affect the
|
||||
scheduler's behavior. Therefore, such
|
||||
modifications should only be performed
|
||||
through an access method. >*/
|
||||
{
|
||||
// Of course, it's only worth reshuffling the queue and all if we're
|
||||
// actually changing the priority.
|
||||
@@ -35,12 +44,15 @@ public:
|
||||
// program; it has nothing to do with implementing scheduler priority.
|
||||
// This is a public data member -- not requiring set/get access methods --
|
||||
// because we need not inform the scheduler of any change.
|
||||
std::string name;
|
||||
std::string name; /*< A property that does not affect the scheduler does
|
||||
not need access methods. >*/
|
||||
|
||||
private:
|
||||
int priority_;
|
||||
};
|
||||
//]
|
||||
|
||||
//[priority_scheduler
|
||||
class priority_scheduler:
|
||||
public boost::fibers::sched_algorithm_with_properties<priority_props>
|
||||
{
|
||||
@@ -57,9 +69,14 @@ public:
|
||||
|
||||
// For a subclass of sched_algorithm_with_properties<>, it's important to
|
||||
// override the correct awakened() overload.
|
||||
/*<< You must override the [member_link sched_algorithm_with_properties..awakened]
|
||||
method. This is how your scheduler receives notification of a
|
||||
fiber that has become ready to run. >>*/
|
||||
virtual void awakened(boost::fibers::fiber_context* f, priority_props& props)
|
||||
{
|
||||
int f_priority = props.get_priority();
|
||||
int f_priority = props.get_priority(); /*< `props` is the instance of
|
||||
priority_props associated
|
||||
with the passed fiber `f`. >*/
|
||||
// With this scheduler, fibers with higher priority values are
|
||||
// preferred over fibers with lower priority values. But fibers with
|
||||
// equal priority values are processed in round-robin fashion. So when
|
||||
@@ -68,17 +85,24 @@ public:
|
||||
// the queue with LOWER priority, and insert before that one.
|
||||
boost::fibers::fiber_context** fp = &head_;
|
||||
for ( ; *fp; fp = &(*fp)->nxt)
|
||||
if (properties(*fp).get_priority() < f_priority)
|
||||
if (properties(*fp).get_priority() < f_priority) /*< Use the
|
||||
[member_link sched_algorithm_with_properties..properties]
|
||||
method to access properties for any ['other] fiber. >*/
|
||||
break;
|
||||
// It doesn't matter whether we hit the end of the list or found
|
||||
// another fiber with lower priority. Either way, insert f here.
|
||||
f->nxt = *fp;
|
||||
f->nxt = *fp; /*< Note use of the [data_member_link fiber_context..nxt] member. >*/
|
||||
*fp = f;
|
||||
//<-
|
||||
|
||||
std::cout << "awakened(" << props.name << "): ";
|
||||
describe_ready_queue();
|
||||
//->
|
||||
}
|
||||
|
||||
/*<< You must override the [member_link sched_algorithm_with_properties..pick_next]
|
||||
method. This is how your scheduler actually advises the fiber manager
|
||||
of the next fiber to run. >>*/
|
||||
virtual boost::fibers::fiber_context* pick_next()
|
||||
{
|
||||
// if ready queue is empty, just tell caller
|
||||
@@ -89,11 +113,15 @@ public:
|
||||
head_ = f->nxt;
|
||||
f->nxt = nullptr;
|
||||
|
||||
//<-
|
||||
std::cout << "pick_next() resuming " << properties(f).name << ": ";
|
||||
describe_ready_queue();
|
||||
//->
|
||||
return f;
|
||||
}
|
||||
|
||||
/*<< You must override [member_link sched_algorithm_with_properties..ready_fibers]
|
||||
to inform the fiber manager of the size of your ready queue. >>*/
|
||||
virtual std::size_t ready_fibers() const noexcept
|
||||
{
|
||||
std::size_t count = 0;
|
||||
@@ -104,14 +132,20 @@ public:
|
||||
return count;
|
||||
}
|
||||
|
||||
/*<< Overriding [member_link sched_algorithm_with_properties..property_change]
|
||||
is optional. This override handles the case in which the running
|
||||
fiber changes the priority of another ready fiber: a fiber already in
|
||||
our queue. In that case, move the updated fiber within the queue. >>*/
|
||||
virtual void property_change(boost::fibers::fiber_context* f, priority_props& props)
|
||||
{
|
||||
// Although our priority_props class defines multiple properties, only
|
||||
// one of them (priority) actually calls notify() when changed. The
|
||||
// point of a property_change() override is to reshuffle the ready
|
||||
// queue according to the updated priority value.
|
||||
//<-
|
||||
std::cout << "property_change(" << props.name << '(' << props.get_priority()
|
||||
<< ")): ";
|
||||
//->
|
||||
|
||||
// Despite the added complexity of the loop body, make a single pass
|
||||
// over the queue to find both the existing item and the new desired
|
||||
@@ -152,31 +186,44 @@ public:
|
||||
// possible to get a property_change() call for a fiber that
|
||||
// is_ready() but is not yet on our ready queue. If it's not there, no
|
||||
// action required: we'll handle it next time it hits awakened().
|
||||
if (! found)
|
||||
if (! found) /*< Your `property_change()` override must be able to
|
||||
handle the case in which the passed `f` is not in
|
||||
your ready queue. It might be running, or it might be
|
||||
blocked. >*/
|
||||
{
|
||||
//<-
|
||||
// hopefully user will distinguish this case by noticing that
|
||||
// the fiber with which we were called does not appear in the
|
||||
// ready queue at all
|
||||
describe_ready_queue();
|
||||
//->
|
||||
return;
|
||||
}
|
||||
// There might not be any ready fibers with lower priority. In that
|
||||
// case, append to the end of the queue.
|
||||
/*=if (! insert)*/
|
||||
//<-
|
||||
std::string where;
|
||||
if (insert)
|
||||
where = std::string("before ") + properties(*insert).name;
|
||||
else
|
||||
//->
|
||||
{
|
||||
insert = fp;
|
||||
//<-
|
||||
where = "to end";
|
||||
//->
|
||||
}
|
||||
// Insert f at the new insertion point in the queue.
|
||||
f->nxt = *insert;
|
||||
*insert = f;
|
||||
//<-
|
||||
|
||||
std::cout << "moving " << where << ": ";
|
||||
describe_ready_queue();
|
||||
//->
|
||||
}
|
||||
//<-
|
||||
|
||||
void describe_ready_queue()
|
||||
{
|
||||
@@ -194,14 +241,18 @@ public:
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
//->
|
||||
};
|
||||
//]
|
||||
|
||||
//[init
|
||||
void init(const std::string& name, int priority)
|
||||
{
|
||||
priority_props& props(boost::this_fiber::properties<priority_props>());
|
||||
props.name = name;
|
||||
props.set_priority(priority);
|
||||
}
|
||||
//]
|
||||
|
||||
void yield_fn(const std::string& name, int priority)
|
||||
{
|
||||
@@ -225,13 +276,16 @@ void barrier_fn(const std::string& name, int priority, boost::fibers::barrier& b
|
||||
std::cout << "fiber " << name << " done" << std::endl;
|
||||
}
|
||||
|
||||
//[change_fn
|
||||
void change_fn(const std::string& name, int priority,
|
||||
boost::fibers::fiber& other, int other_priority,
|
||||
boost::fibers::barrier& barrier)
|
||||
{
|
||||
init(name, priority);
|
||||
|
||||
//<-
|
||||
std::cout << "fiber " << name << " waiting on barrier" << std::endl;
|
||||
//->
|
||||
barrier.wait();
|
||||
// We assume a couple things about 'other':
|
||||
// - that it was also waiting on the same barrier
|
||||
@@ -239,17 +293,26 @@ void change_fn(const std::string& name, int priority,
|
||||
// If both are true, 'other' is now ready to run but is sitting in
|
||||
// priority_scheduler's ready queue. Change its priority.
|
||||
priority_props& other_props(other.properties<priority_props>());
|
||||
//<-
|
||||
std::cout << "fiber " << name << " changing priority of " << other_props.name
|
||||
<< " to " << other_priority << std::endl;
|
||||
//->
|
||||
other_props.set_priority(other_priority);
|
||||
//<-
|
||||
std::cout << "fiber " << name << " done" << std::endl;
|
||||
//->
|
||||
}
|
||||
//]
|
||||
|
||||
//[main
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// make sure we use our priority_scheduler rather than default round_robin
|
||||
priority_scheduler psched;
|
||||
boost::fibers::set_scheduling_algorithm(&psched);
|
||||
/*= ...*/
|
||||
/*=}*/
|
||||
//]
|
||||
|
||||
{
|
||||
// verify that high-priority fiber always gets scheduled first
|
||||
|
||||
@@ -80,9 +80,22 @@ void test_hwm_less_lwm()
|
||||
BOOST_CHECK( thrown);
|
||||
}
|
||||
|
||||
void test_hwm_equal_lwm()
|
||||
{
|
||||
bool thrown = false;
|
||||
try {
|
||||
boost::fibers::bounded_channel< int > c( 3, 3);
|
||||
} catch ( boost::fibers::fiber_exception const&) {
|
||||
thrown = true;
|
||||
}
|
||||
BOOST_CHECK( thrown);
|
||||
}
|
||||
|
||||
void test_push()
|
||||
{
|
||||
boost::fibers::bounded_channel< int > c( 10);
|
||||
BOOST_CHECK_EQUAL( c.upper_bound(), 10u );
|
||||
BOOST_CHECK_EQUAL( c.lower_bound(), 9u );
|
||||
BOOST_CHECK( boost::fibers::channel_op_status::success == c.push( 1) );
|
||||
}
|
||||
|
||||
@@ -110,6 +123,7 @@ void test_try_push_closed()
|
||||
void test_try_push_full()
|
||||
{
|
||||
boost::fibers::bounded_channel< int > c( 1);
|
||||
BOOST_CHECK_EQUAL( c.lower_bound(), 0u );
|
||||
BOOST_CHECK( boost::fibers::channel_op_status::success == c.try_push( 1) );
|
||||
BOOST_CHECK( boost::fibers::channel_op_status::full == c.try_push( 2) );
|
||||
}
|
||||
@@ -390,23 +404,23 @@ void test_wm_1()
|
||||
});
|
||||
boost::fibers::fiber f2([&c,&ids](){
|
||||
ids.push_back( boost::this_fiber::get_id() );
|
||||
BOOST_CHECK( 1 == c.value_pop() );
|
||||
BOOST_CHECK_EQUAL( 1, c.value_pop() );
|
||||
|
||||
// let other fiber run
|
||||
boost::this_fiber::yield();
|
||||
|
||||
ids.push_back( boost::this_fiber::get_id() );
|
||||
BOOST_CHECK( 2 == c.value_pop() );
|
||||
BOOST_CHECK_EQUAL( 2, c.value_pop() );
|
||||
|
||||
ids.push_back( boost::this_fiber::get_id() );
|
||||
BOOST_CHECK( 3 == c.value_pop() );
|
||||
BOOST_CHECK_EQUAL( 3, c.value_pop() );
|
||||
|
||||
ids.push_back( boost::this_fiber::get_id() );
|
||||
BOOST_CHECK( 4 == c.value_pop() );
|
||||
BOOST_CHECK_EQUAL( 4, c.value_pop() );
|
||||
|
||||
ids.push_back( boost::this_fiber::get_id() );
|
||||
// would block because channel is empty
|
||||
BOOST_CHECK( 5 == c.value_pop() );
|
||||
BOOST_CHECK_EQUAL( 5, c.value_pop() );
|
||||
|
||||
ids.push_back( boost::this_fiber::get_id() );
|
||||
});
|
||||
@@ -414,7 +428,7 @@ void test_wm_1()
|
||||
boost::fibers::fiber::id id2 = f2.get_id();
|
||||
f1.join();
|
||||
f2.join();
|
||||
BOOST_CHECK( 12 == ids.size() );
|
||||
BOOST_CHECK_EQUAL( 12u, ids.size() );
|
||||
BOOST_CHECK_EQUAL( id1, ids[0]); // f1 pushes 1
|
||||
BOOST_CHECK_EQUAL( id1, ids[1]); // f1 pushes 2
|
||||
BOOST_CHECK_EQUAL( id1, ids[2]); // f1 pushes 3
|
||||
@@ -455,25 +469,25 @@ void test_wm_2()
|
||||
});
|
||||
boost::fibers::fiber f2([&c,&ids](){
|
||||
ids.push_back( boost::this_fiber::get_id() );
|
||||
BOOST_CHECK( 1 == c.value_pop() );
|
||||
BOOST_CHECK_EQUAL( 1, c.value_pop() );
|
||||
|
||||
// let other fiber run
|
||||
boost::this_fiber::yield();
|
||||
|
||||
ids.push_back( boost::this_fiber::get_id() );
|
||||
BOOST_CHECK( 2 == c.value_pop() );
|
||||
BOOST_CHECK_EQUAL( 2, c.value_pop() );
|
||||
|
||||
// let other fiber run
|
||||
boost::this_fiber::yield();
|
||||
|
||||
ids.push_back( boost::this_fiber::get_id() );
|
||||
BOOST_CHECK( 3 == c.value_pop() );
|
||||
BOOST_CHECK_EQUAL( 3, c.value_pop() );
|
||||
|
||||
ids.push_back( boost::this_fiber::get_id() );
|
||||
BOOST_CHECK( 4 == c.value_pop() );
|
||||
BOOST_CHECK_EQUAL( 4, c.value_pop() );
|
||||
|
||||
ids.push_back( boost::this_fiber::get_id() );
|
||||
BOOST_CHECK( 5 == c.value_pop() );
|
||||
BOOST_CHECK_EQUAL( 5, c.value_pop() );
|
||||
|
||||
ids.push_back( boost::this_fiber::get_id() );
|
||||
});
|
||||
@@ -481,7 +495,7 @@ void test_wm_2()
|
||||
boost::fibers::fiber::id id2 = f2.get_id();
|
||||
f1.join();
|
||||
f2.join();
|
||||
BOOST_CHECK( 12 == ids.size() );
|
||||
BOOST_CHECK_EQUAL( 12u, ids.size() );
|
||||
BOOST_CHECK_EQUAL( id1, ids[0]); // f1 pushes 1
|
||||
BOOST_CHECK_EQUAL( id1, ids[1]); // f1 pushes 2
|
||||
BOOST_CHECK_EQUAL( id1, ids[2]); // f1 pushes 3
|
||||
@@ -499,6 +513,8 @@ void test_wm_2()
|
||||
void test_wm_3()
|
||||
{
|
||||
boost::fibers::bounded_channel< int > c( 3, 1);
|
||||
BOOST_CHECK_EQUAL( c.upper_bound(), 3u );
|
||||
BOOST_CHECK_EQUAL( c.lower_bound(), 1u );
|
||||
std::vector< boost::fibers::fiber::id > ids;
|
||||
boost::fibers::fiber f1([&c,&ids](){
|
||||
ids.push_back( boost::this_fiber::get_id() );
|
||||
@@ -521,25 +537,25 @@ void test_wm_3()
|
||||
});
|
||||
boost::fibers::fiber f2([&c,&ids](){
|
||||
ids.push_back( boost::this_fiber::get_id() );
|
||||
BOOST_CHECK( 1 == c.value_pop() );
|
||||
BOOST_CHECK_EQUAL( 1, c.value_pop() );
|
||||
|
||||
// let other fiber run
|
||||
boost::this_fiber::yield();
|
||||
|
||||
ids.push_back( boost::this_fiber::get_id() );
|
||||
BOOST_CHECK( 2 == c.value_pop() );
|
||||
BOOST_CHECK_EQUAL( 2, c.value_pop() );
|
||||
|
||||
// let other fiber run
|
||||
boost::this_fiber::yield();
|
||||
|
||||
ids.push_back( boost::this_fiber::get_id() );
|
||||
BOOST_CHECK( 3 == c.value_pop() );
|
||||
BOOST_CHECK_EQUAL( 3, c.value_pop() );
|
||||
|
||||
ids.push_back( boost::this_fiber::get_id() );
|
||||
BOOST_CHECK( 4 == c.value_pop() );
|
||||
BOOST_CHECK_EQUAL( 4, c.value_pop() );
|
||||
|
||||
ids.push_back( boost::this_fiber::get_id() );
|
||||
BOOST_CHECK( 5 == c.value_pop() );
|
||||
BOOST_CHECK_EQUAL( 5, c.value_pop() );
|
||||
|
||||
ids.push_back( boost::this_fiber::get_id() );
|
||||
});
|
||||
@@ -547,7 +563,7 @@ void test_wm_3()
|
||||
boost::fibers::fiber::id id2 = f2.get_id();
|
||||
f1.join();
|
||||
f2.join();
|
||||
BOOST_CHECK( 12 == ids.size() );
|
||||
BOOST_CHECK_EQUAL( 12u, ids.size() );
|
||||
BOOST_CHECK_EQUAL( id1, ids[0]); // f1 pushes 1
|
||||
BOOST_CHECK_EQUAL( id1, ids[1]); // f1 pushes 2
|
||||
BOOST_CHECK_EQUAL( id1, ids[2]); // f1 pushes 3
|
||||
@@ -584,26 +600,26 @@ void test_wm_4()
|
||||
});
|
||||
boost::fibers::fiber f2([&c,&ids](){
|
||||
ids.push_back( boost::this_fiber::get_id() );
|
||||
BOOST_CHECK( 1 == c.value_pop() );
|
||||
BOOST_CHECK_EQUAL( 1, c.value_pop() );
|
||||
|
||||
// let potential other fibers run
|
||||
boost::this_fiber::yield();
|
||||
|
||||
ids.push_back( boost::this_fiber::get_id() );
|
||||
BOOST_CHECK( 2 == c.value_pop() );
|
||||
BOOST_CHECK_EQUAL( 2, c.value_pop() );
|
||||
|
||||
// let potential other fibers run
|
||||
boost::this_fiber::yield();
|
||||
|
||||
ids.push_back( boost::this_fiber::get_id() );
|
||||
BOOST_CHECK( 3 == c.value_pop() );
|
||||
BOOST_CHECK_EQUAL( 3, c.value_pop() );
|
||||
|
||||
// let potential other fibers run
|
||||
boost::this_fiber::yield();
|
||||
|
||||
ids.push_back( boost::this_fiber::get_id() );
|
||||
// would block because channel is empty
|
||||
BOOST_CHECK( 4 == c.value_pop() );
|
||||
BOOST_CHECK_EQUAL( 4, c.value_pop() );
|
||||
|
||||
ids.push_back( boost::this_fiber::get_id() );
|
||||
});
|
||||
@@ -611,7 +627,7 @@ void test_wm_4()
|
||||
boost::fibers::fiber::id id2 = f2.get_id();
|
||||
f1.join();
|
||||
f2.join();
|
||||
BOOST_CHECK( 10 == ids.size() );
|
||||
BOOST_CHECK_EQUAL( 10u, ids.size() );
|
||||
BOOST_CHECK_EQUAL( id1, ids[0]); // f1 pushes 1
|
||||
BOOST_CHECK_EQUAL( id1, ids[1]); // f1 pushes 2
|
||||
BOOST_CHECK_EQUAL( id1, ids[2]); // f1 pushes 3
|
||||
@@ -650,6 +666,7 @@ boost::unit_test::test_suite * init_unit_test_suite( int, char* [])
|
||||
test->add( BOOST_TEST_CASE( & test_zero_wm_1) );
|
||||
test->add( BOOST_TEST_CASE( & test_zero_wm_2) );
|
||||
test->add( BOOST_TEST_CASE( & test_hwm_less_lwm) );
|
||||
test->add( BOOST_TEST_CASE( & test_hwm_equal_lwm) );
|
||||
test->add( BOOST_TEST_CASE( & test_push) );
|
||||
test->add( BOOST_TEST_CASE( & test_push_closed) );
|
||||
test->add( BOOST_TEST_CASE( & test_try_push) );
|
||||
|
||||
Reference in New Issue
Block a user