mirror of
https://github.com/dlang/phobos.git
synced 2025-05-10 14:08:32 +03:00
207 lines
3.9 KiB
D
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");
|
|
}
|
|
|