diff --git a/doc/channel.qbk b/doc/channel.qbk index 8122f42e..4e393f0a 100644 --- a/doc/channel.qbk +++ b/doc/channel.qbk @@ -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]] diff --git a/doc/customization.qbk b/doc/customization.qbk index f37c67e1..14ac1e9e 100644 --- a/doc/customization.qbk +++ b/doc/customization.qbk @@ -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().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] diff --git a/doc/fiber.qbk b/doc/fiber.qbk index 11fa63f0..795a65a6 100644 --- a/doc/fiber.qbk +++ b/doc/fiber.qbk @@ -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] diff --git a/doc/fibers.qbk b/doc/fibers.qbk index 31553f6a..92fc980b 100644 --- a/doc/fibers.qbk +++ b/doc/fibers.qbk @@ -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]()]] diff --git a/doc/scheduling.qbk b/doc/scheduling.qbk index 3c83e7e1..236b3af4 100644 --- a/doc/scheduling.qbk +++ b/doc/scheduling.qbk @@ -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] diff --git a/examples/priority.cpp b/examples/priority.cpp index 2a9bc879..1e9bab4c 100644 --- a/examples/priority.cpp +++ b/examples/priority.cpp @@ -8,19 +8,28 @@ #include #include +//[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 { @@ -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()); 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()); +//<- 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 diff --git a/test/test_bounded_channel.cpp b/test/test_bounded_channel.cpp index 39526d3a..844e0691 100644 --- a/test/test_bounded_channel.cpp +++ b/test/test_bounded_channel.cpp @@ -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) );