MPRES introduces the resource and source datatypes. The resource is
merely a mailbox datatype, used for storing the available sources of the
resource as messages. The source datatype is a message with two extra
fields. The first specifies the task which owns the source (or NIL if
none) and the second is a double-pointer structure (same as the
mpmsg header) which keeps all the sources of a resource in a ring.
Operations are provided to take (
mpres()) and give or release
(
mpres
()) a source of a resource; they are implemented directly
with
mptsk
() and
mptsk
(). Additional operations are
provided to initialize a resource of a particular type, and to
deinitialize it (see MAN page for exact details).
The two fields allow all sources (and users) of a resource to be located. Though this feature is not taken advantage of, it could be useful for reclaiming resources from crashed or discourteous tasks. By incorporating the knowledge gained from the waitingfor and owns field of tasks, its also possible to detect deadlock (a cycle of tasks waiting endlessly for each other) before it occurs; when a task is about to be suspended waitingfor a resource, it first checks if any other tasks which have the resource are directly or indirectly already waitingfor it. Because this check potentially involves a great deal of searching for every access to a resource, it is not implemented, but could be, if deadlock is a serious problem. (Nevertheless, users of Mailbox should beware of the base case of deadlock, where a task requests a resource that it already has.)
The MPRES layer defines a few general resources. The most universal
resource is
mpres. With memory contention management, we
can finally write a one-argument
mpthd
():
MPTHDID mpthd_create(MPTHDFN mpthdfn) { int mydeftasksize= mpthd_mydef(mpdef_tasksize,int); MPTHDID mpthd; mpres_take(mpres_memory); /* CLAIM MEMORY RESOURCE */ mpthd= (MPTHDID)malloc(mydeftasksize); mpres_give(mpres_memory); /* RELEASE MEMORY RESOURCE */ if (mpthd) mpthd_init(mpthd,mydeftasksize,mpthdfn); return(mpthd); }
Notice how the resource must be claimed and released around the
standard malloc memory allocation call. This insures no other task
can allocate memory at the same time. Likewise protection must be
taken around the free memory deallocation call and other memory
management calls. In general, every ``C'' function which
accesses resources must be executed within an appropriate resource
lock. This is true for any concurrent system. To make the job
easier, the MPRES package defines a macro which takes as arguments a
resource and a block of code; it claims the resource, executes the
code, then (remembers to) free the resource before exiting. MPRES
also duplicates many of the common ``C'' function calls with this
extra locking added (eg,
mpres()). Operations such as
malloc(), which are completed in a single transaction, are well
suited for this; in contrast, operations as printf usually require
several calls (``or the'' ``Hello'' ``output may not stick''
``Goodbye'' ``together''), and are best locked explicitly by the
caller. Explicit locking is also necessary when calling (library)
functions which use resources and are unaware they are executing in a
concurrent environment.
One way to simplify the complexities of having to lock and unlock resources is to assume a uniprocessor environment (often the case) and have tasks only yield control explicitly, when they are not holding resources. This may be an acceptable compromise in many circumstances, and Mailbox allows for this style of programming.
MPRES also provides three levels of I/O locking granularity.
On the high stream level are the standard streams:
mpres,
mpres
, and
mpres
. On the middle component level
are standard operating system components:
mpres
,
mpres
,
mpres
and
mpres
. And on
the low device level are standard devices:
mpres
,
mpres
,
mpres
,
mpres
,
mpres
,
and
mpres
. Locking at a lower granularity level allows for
higher degree of concurrency at a cost of complexity and portability.
These locking levels are likely to be incomplete and to overlap; more
than anything else, they are merely suggested names for resources and
their locks. It is up to the programmer to tailor the locking
appropriate to his environment and task.