2
0
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:
Oliver Kowalke
2015-08-17 08:04:35 +02:00
7 changed files with 253 additions and 67 deletions

View File

@@ -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]]

View File

@@ -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]

View File

@@ -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]

View File

@@ -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]()]]

View File

@@ -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]

View File

@@ -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

View File

@@ -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) );