phobos/std/synchro.d
2008-07-08 03:26:38 +00:00

207 lines
3.9 KiB
D

// Written in the D programming language
// Put in the public domain by Bartosz Milewski
/**
Defines thread synchronization primitives.
Note:
A lot of synchronization can be done using built-in monitors
by defining $(D synchronized) methods.
(every object is a potential monitor--behavior inherited from $(D Object).)
Use std.synchro for those cases
where simple monitors are not sufficient.
Author: Bartosz Milewski
Macros:
WIKI=Phobos/StdSynchro
*/
module std.synchro;
import std.c.stdio;
//debug=thread;
/**
Defines a common interface for lockable objects (e.g., mutexes).
*/
interface Lockable
{
void lock();
void unlock();
}
/**
Scoped object that locks a $(D Lockable) for the duration of a scope.
Note:
If the mutex is associated with an object, and the lock is taken for the
whole scope of a method, it's probably better to declare the method sd $(D synchronized)
and use the mutex that's built into every $(D Object).
Example:
----
import std.synchro;
Mutex mtx;
static this()
{
mtx = new Mutex;
}
void f()
{
scope lock = new Lock(mtx);
// access shared data
// end of scope: mutex released
}
----
*/
scope class Lock
{
public:
this (Lockable mtx)
{
_mtx = mtx;
_mtx.lock ();
}
~this ()
{
_mtx.unlock ();
}
private:
Lockable _mtx;
}
/* ================================ Windows ================================= */
version (Windows)
{
private import std.c.windows.windows;
/**
Implements mutual exclusion. $(D Mutex) is re-entrant, i.e.,
the same thread may lock it multiple times.
It must unlock it the same number of times.
Note: On Windows, it's implemented as $(D CriticalSection); on Linux, using pthreads.
Example: See the $(D Lock) example
*/
class Mutex: Lockable
{
public:
this()
{
InitializeCriticalSection (&_critSection);
}
~this()
{
DeleteCriticalSection (&_critSection);
}
override void lock()
{
EnterCriticalSection (&_critSection);
}
override void unlock()
{
LeaveCriticalSection (&_critSection);
}
/**
Returns $(D true) if lock taken.
*/
bool trylock()
{
return TryEnterCriticalSection(&_critSection) != 0; // lock taken
}
private:
CRITICAL_SECTION _critSection;
}
} // Windows
/* ================================ linux ================================= */
version (linux)
{
private import std.c.linux.linux;
private import std.c.linux.linuxextern;
extern(C) {
extern pthread_mutexattr_t _monitors_attr;
}
class Mutex: Lockable
{
public:
this()
{
pthread_mutex_init(&_mtx, &_monitors_attr);
}
~this()
{
pthread_mutex_destroy(&_mtx);
}
override void lock()
{
pthread_mutex_lock(&_mtx);
}
override void unlock()
{
pthread_mutex_unlock(&_mtx);
}
bool trylock()
{
return pthread_mutex_trylock(&_mtx) == 0; // lock taken
}
private:
pthread_mutex_t _mtx;
}
} // linux
version(unittest)
{
import std.synchro;
import std.thread;
import std.stdio;
int glob;
}
unittest
{
//writeln("_monitors_attr = ", _monitors_attr.__mutexkind);
Mutex mtx = new Mutex;
void inc_glob_twice()
{
scope lock = new Lock(mtx);
assert(glob % 2 == 0);
glob++;
{
// Test lock re-entrancy
scope lock2 = new Lock(mtx);
glob++;
}
}
int f()
{
for (int i = 0; i < 1000; ++i)
inc_glob_twice();
return 0;
}
auto thr1 = new Thread(&f);
auto thr2 = new Thread(&f);
thr1.start;
thr2.start;
thr1.wait;
thr2.wait;
//writeln("end synchro");
}