123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142(*
* RMutex - Reentrant mutexes
* Copyright (C) 2008 David Teller, LIFO, Universite d'Orleans
* 2011 Edgar Friendly <thelema314@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version,
* with the special exception on linking described in file LICENSE.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*)moduleBaseRMutex=structtypeowner={thread:int;(**Identity of the latest owner (possibly the current owner)*)mutabledepth:int(**Number of times the current owner owns the lock.*)}typet={primitive:Mutex.t;(**A low-level mutex, used to protect access to [ownership]*)wait:Condition.t;(** a condition to wait on when the lock is locked *)mutableownership:owneroption;}letcreate()={primitive =Mutex.create();wait=Condition.create();ownership=None}(**
Attempt to acquire the mutex, waiting indefinitely
*)letlockm=letid=Thread.id(Thread.self())inMutex.lockm.primitive;(******Critical section begins*)(matchm.ownershipwith|None->(*Lock belongs to nobody, I can take it. *)m.ownership<-Some{thread=id;depth=1}|Someswhens.thread=id->(*Lockalready belongs to me, I can keep it. *)s.depth<-s.depth+1|_->(*Lockbelongs to someone else. *)whilenot(m.ownership=None)doCondition.waitm.waitm.primitivedone;m.ownership<-Some{thread=id;depth=1});Mutex.unlockm.primitive(******Critical section ends*)(** Attempt to acquire the mutex, returning true if successful. If
waiting would be required, return false instead.
*)lettry_lockm=letid=Thread.id(Thread.self())inMutex.lockm.primitive;(******Critical section begins*)letr=matchm.ownershipwith|None->(*Lock belongs to nobody, I can take it. *)m.ownership<-Some{thread=id;depth=1};true|Someswhens.thread=id->(*Lockalready belongs to me, I can keep it. *)s.depth<-s.depth+1;true|_->(*Lock belongs to someone else. *)false(* give up *)inMutex.unlockm.primitive;(******Critical section ends*)r(** Unlock the mutex; this function checks that the thread calling
unlock is the owner and raises an assertion failure if this is not
the case. It will also raise an assertion failure if the mutex is
not locked. *)letunlockm=letid=Thread.id(Thread.self())inMutex.lockm.primitive;(******Critical section begins*)(matchm.ownershipwith|Somes->assert(s.thread=id);(*If I'm not theowner, we have a consistency issue.*)ifs.depth>1thens.depth<-s.depth-1(*release one depthbut we're still the owner*)elsebeginm.ownership<-None;(*release once and for all*)Condition.signalm.wait(*wake up waiting threads *)end|_->assertfalse);Mutex.unlockm.primitive(******Critical section ends *)endmoduleLock=BatConcurrent.MakeLock(BaseRMutex)includeBaseRMutexletmake=Lock.makeletsynchronize=Lock.synchronize(*let synchronize ?lock:(l=create ()) f = fun x ->
lock l;
try
let result = f x
in lock l;
result
with e ->
lock l;
raise e*)(*$R create; lock; unlock
let test num_threads work_per_thread =
let l = create () in
let count = ref 0 in
let worker n = for i = 1 to work_per_thread do
lock l; lock l; Thread.delay 0.001; incr count;
unlock l; Thread.delay 0.0001; unlock l;
done in
let children = Array.init num_threads (Thread.create worker) in
Array.iter Thread.join children;
!count
in
assert_equal (30*30) (test 30 30) ~printer:string_of_int
*)