#include <x/semaphore.H> class testownerObj : public x::semaphore::base::ownerObj { public: // ... void process(const x::ref<x::obj> &mcguffin) override; // ... }; typedef x::ref<testownerObj> testowner; testowner owner=testowner::create(); x::property::value<size_t> maxjobs("maxjobs", 10); x::semaphore sem(x::semaphore::create(maxjobs)); sem->request(owner, 1);
x::semaphore
is one of two semaphore-oriented design patterns. This class implements
a semaphore whose size comes from an application property,
and with a queue of objects that are waiting to acquire the semaphore.
x::semaphore
is a
reference-counted object.
The create
() method creates the semaphore, its
parameter is a property that specifies the maximum value of the
semaphore counter, 10 in the above example.
Acquiring ownership of the semaphore increments the semaphore counter.
Releasing ownership decrements the counter.
request
() submits a request to acquire the
semaphore. The first parameter is a x::ref
to a subclass of
x::semaphore::base::ownerObj
,
a reference-counted object which
defines process
(), which gets invoked when
the requested ownership' gets acquired.
process
() receives a
mcguffin. The ownership of the semaphore
does not necessarily get released when process
()
returns, but rather when the mcguffin goes out of scope and gets
destroyed. If process
() returns without touching
the mcguffin, it goes out of scope, gets destroyed, and the acquired
ownership of the semaphore gets released automatically upon return from
process
.
If process
() stashes the mcguffin away,
somewhere,
the ownership of the semaphore remains with the mcguffin, and gets
released only when mcguffin goes out of scope and gets destroyed.
The second parameter to request
() specifies by how
much the semaphore gets incremented (not necessarily 1, but t cannot be
zero). If this can
be done and the semaphore's count remains at or below the maximum value,
the ownership gets acquired immediately, and the owner's
process
() gets invoked immediately.
Otherwise, request
() returns, and
process
() gets invoked later, after other owners
release their ownership and the semaphore's counter gets decremented
sufficiently. The list of requested owners is maintained in a
FIFO-ordered queue.
The semaphore keeps only a weak reference on the requested owners. The
owner object can go out of scope and gets destroyed without its
process
() ever getting invoked.
Every thread that calls request
(), and every thread
where the last reference to the ownership mcguffin goes out of scope,
gets co-opted in processing waiting owners and invoking their
process
() method. process
()
should not be a time-consuming operation. Blocking and waiting, in
process
() should be avoided.
The value of the semaphore counter property may be changed, and the new maximum value takes effect immediately, in general. If the new maximum semaphore counter exceeds the new semaphore count, no more owners get acquired until the semaphore count goes down below the new maximum value. If the new maximum value gets increased, pending owners do not get acquired immediately. This happens the next time a new owner is requested, or an existing ownership mcguffin goes out of scope; at which time as many owners get acquired as possible, given the new maximum semaphore count.
x::semaphore::base::owner owner= x::semaphore::base::owner::create( [] (const x::ref<x::obj> &mcguffin) { // ... }); s->request(owner, 1);
The semaphore owner object class supplies a
create
() that takes a functor and constructs
an owner object that invokes the given functor from its
process
().