1 /*
2  *  Copyright (c) 2012-2014, Bruno Levy
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *
8  *  * Redistributions of source code must retain the above copyright notice,
9  *  this list of conditions and the following disclaimer.
10  *  * Redistributions in binary form must reproduce the above copyright notice,
11  *  this list of conditions and the following disclaimer in the documentation
12  *  and/or other materials provided with the distribution.
13  *  * Neither the name of the ALICE Project-Team nor the names of its
14  *  contributors may be used to endorse or promote products derived from this
15  *  software without specific prior written permission.
16  *
17  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  *  POSSIBILITY OF SUCH DAMAGE.
28  *
29  *  If you modify this software, you should include a notice giving the
30  *  name of the person performing the modification, the date of modification,
31  *  and the reason for such modification.
32  *
33  *  Contact: Bruno Levy
34  *
35  *     Bruno.Levy@inria.fr
36  *     http://www.loria.fr/~levy
37  *
38  *     ALICE Project
39  *     LORIA, INRIA Lorraine,
40  *     Campus Scientifique, BP 239
41  *     54506 VANDOEUVRE LES NANCY CEDEX
42  *     FRANCE
43  *
44  */
45 
46 #ifndef GEOGRAM_BASIC_THREAD_SYNC
47 #define GEOGRAM_BASIC_THREAD_SYNC
48 
49 #include <geogram/basic/common.h>
50 #include <geogram/basic/atomics.h>
51 #include <geogram/basic/numeric.h>
52 #include <geogram/basic/assert.h>
53 #include <geogram/basic/argused.h>
54 #include <vector>
55 
56 #ifdef GEO_OS_APPLE
57 # define GEO_USE_DEFAULT_SPINLOCK_ARRAY
58 # include <AvailabilityMacros.h>
59 # if defined(MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12
60 #   include <os/lock.h>
61 # endif
62 #endif
63 
64 #ifdef geo_debug_assert
65 #define geo_thread_sync_assert(x) geo_debug_assert(x)
66 #else
67 #define geo_thread_sync_assert(x)
68 #endif
69 
70 /**
71  * \file geogram/basic/thread_sync.h
72  * \brief Functions and classes for process manipulation
73  */
74 
75 namespace GEO {
76 
77     namespace Process {
78 
79 #if defined(GEO_OS_RASPBERRY)
80 
81         /** A lightweight synchronization structure. */
82         typedef arm32_mutex_t spinlock;
83 
84         /** The initialization value of a spin lock. */
85 #       define GEOGRAM_SPINLOCK_INIT 0
86         /**
87          * \brief Loops until \p x is available then reserves it.
88          * \param[in] x a spinlock that should be available.
89          */
acquire_spinlock(spinlock & x)90         inline void acquire_spinlock(spinlock& x) {
91             lock_mutex_arm32(&x);
92         }
93 
94         /**
95          * \brief Makes \p x available to other threads.
96          * \param[in] x a spinlock that should be reserved.
97          */
release_spinlock(spinlock & x)98         inline void release_spinlock(spinlock& x) {
99             unlock_mutex_arm32(&x);
100         }
101 
102 #elif defined(GEO_OS_ANDROID)
103 
104         /** A lightweight synchronization structure. */
105         typedef android_mutex_t spinlock;
106 
107         /** The initialization value of a spin lock. */
108 #       define GEOGRAM_SPINLOCK_INIT 0
109         /**
110          * \brief Loops until \p x is available then reserves it.
111          * \param[in] x a spinlock that should be available.
112          */
113         inline void acquire_spinlock(spinlock& x) {
114             lock_mutex_android(&x);
115         }
116 
117         /**
118          * \brief Makes \p x available to other threads.
119          * \param[in] x a spinlock that should be reserved.
120          */
121         inline void release_spinlock(spinlock& x) {
122             unlock_mutex_android(&x);
123         }
124 
125 #elif defined(GEO_OS_LINUX) || defined(GEO_OS_DRAGONFLY) || defined(GEO_COMPILER_MINGW)
126 
127         /** A lightweight synchronization structure. */
128         typedef unsigned char spinlock;
129 
130         /** The initialization value of a spin lock. */
131 #       define GEOGRAM_SPINLOCK_INIT 0
132         /**
133          * \brief Loops until \p x is available then reserve it.
134          * \param[in] x a spinlock that should be available.
135          */
136         inline void acquire_spinlock(volatile spinlock& x) {
137             while(__sync_lock_test_and_set(&x, 1) == 1) {
138                 // Intel recommends to have a PAUSE asm instruction
139                 // in the spinlock loop.
140                 geo_pause();
141             }
142         }
143 
144         /**
145          * \brief Makes \p x available to other threads.
146          * \param[in] x a spinlock that should be reserved.
147          */
148         inline void release_spinlock(volatile spinlock& x) {
149             // Note: Since on intel processor, memory writes
150             // (of data types <= bus size) are atomic, we could
151             // simply write 'x=0' instead, but this would
152             // lack the 'memory barrier with release semantics'
153             // required to avoid compiler and/or processor
154             // reordering (important for relaxed memory
155             // models such as Itanium processors).
156             __sync_lock_release(&x);
157         }
158 
159 #elif defined(GEO_OS_APPLE)
160 
161 #if defined(MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12
162         /** A lightweight synchronization structure. */
163         typedef os_unfair_lock spinlock;
164 
165         /** The initialization value of a spin lock. */
166 #       define GEOGRAM_SPINLOCK_INIT OS_UNFAIR_LOCK_INIT
167         //inline void init_spinlock(spinlock & s) { s = OS_UNFAIR_LOCK_INIT; }
168         //inline bool try_acquire_spinlock (spinlock & s) { return os_unfair_lock_trylock(&s); }
169         inline void acquire_spinlock    (spinlock & s) { os_unfair_lock_lock(&s); }
170         inline void release_spinlock  (spinlock & s) { os_unfair_lock_unlock(&s); }
171 #else
172         /** A lightweight synchronization structure. */
173         typedef OSSpinLock spinlock;
174 
175         /** The initialization value of a spin lock. */
176 #       define GEOGRAM_SPINLOCK_INIT OS_SPINLOCK_INIT
177         inline void acquire_spinlock(volatile spinlock& x) {
178             OSSpinLockLock(&x);
179         }
180 
181         inline void release_spinlock(volatile spinlock& x) {
182             OSSpinLockUnlock(&x);
183         }
184 #endif // __MAC_10_12
185 
186 #elif defined(GEO_OS_WINDOWS) && !defined(GEO_COMPILER_MINGW)
187 
188         /** A lightweight synchronization structure. */
189         typedef short spinlock;
190 
191         /** The initialization value of a spin lock. */
192 #       define GEOGRAM_SPINLOCK_INIT 0
193         inline void acquire_spinlock(volatile spinlock& x) {
194             while(_InterlockedCompareExchange16(&x, 1, 0) == 1) {
195                 // Intel recommends to have a PAUSE asm instruction
196                 // in the spinlock loop. Under MSVC/Windows,
197                 // YieldProcessor() is a macro that calls the
198                 // (undocumented) _mm_pause() intrinsic function
199                 // that generates a PAUSE opcode.
200                 YieldProcessor();
201             }
202             // We do not need _ReadBarrier() here since
203             // _InterlockedCompareExchange16
204             // "acts as a full barrier in VC2005" according to the doc
205         }
206 
207         inline void release_spinlock(volatile spinlock& x) {
208             _WriteBarrier();   // prevents compiler reordering
209             x = 0;
210         }
211 
212 #endif
213 
214 #if defined(GEO_USE_DEFAULT_SPINLOCK_ARRAY)
215 
216         // TODO: implement memory-efficient version for
217         // MacOSX MacOSX does have atomic bit
218         // manipulation routines (OSAtomicTestAndSet()),
219         // and also  has OSAtomicAnd32OrigBarrier() and
220         // OSAtomicOr32OrigBarrier() functions that can
221         // be used instead (Orig for 'return previous value'
222         // and Barrier for ('include a memory barrier').
223         // Android needs additional routines in atomics.h/atomics.cpp
224 
225         /**
226          * \brief An array of light-weight synchronisation
227          *  primitives (spinlocks).
228          *
229          * \details This is the reference implementation, that uses
230          * a std::vector of spinlock. There are more efficient
231          * implementations for Linux and Windows.
232          *
233          * \see acquire_spinlock(), release_spinlock()
234          */
235         class SpinLockArray {
236         public:
237             /**
238              * \brief Constructs a new SpinLockArray of size 0.
239              */
SpinLockArray()240             SpinLockArray() {
241             }
242 
243             /**
244              * \brief Constructs a new SpinLockArray of size \p size_in.
245              * \param[in] size_in number of spinlocks in the array.
246              */
SpinLockArray(index_t size_in)247             SpinLockArray(index_t size_in) {
248                 resize(size_in);
249             }
250 
251             /**
252              * \brief Resizes a SpinLockArray.
253              * \details All the spinlocks are reset to 0.
254              * \param[in] size_in The desired new size.
255              */
resize(index_t size_in)256             void resize(index_t size_in) {
257                 spinlocks_.assign(size_in, GEOGRAM_SPINLOCK_INIT);
258             }
259 
260             /**
261              * \brief Resets size to 0 and clears all the memory.
262              */
clear()263             void clear() {
264                 spinlocks_.clear();
265             }
266 
267             /**
268              * \brief Gets the number of spinlocks in this array.
269              */
size()270             index_t size() const {
271                 return index_t(spinlocks_.size());
272             }
273 
274             /**
275              * \brief Acquires a spinlock at a given index
276              * \details Loops until spinlock at index \p i is available then
277              * reserve it.
278              * \param[in] i index of the spinlock
279              */
acquire_spinlock(index_t i)280             void acquire_spinlock(index_t i) {
281                 geo_thread_sync_assert(i < size());
282                 GEO::Process::acquire_spinlock(spinlocks_[i]);
283             }
284 
285             /**
286              * \brief Releases a spinlock at a given index
287              * \details Makes spinlock at index \p i available to other threads.
288              * \param[in] i index of the spinlock
289              */
release_spinlock(index_t i)290             void release_spinlock(index_t i) {
291                 geo_thread_sync_assert(i < size());
292                 GEO::Process::release_spinlock(spinlocks_[i]);
293             }
294 
295         private:
296             std::vector<spinlock> spinlocks_;
297         };
298 
299 #elif defined(GEO_OS_RASPBERRY)
300 
301         /**
302          * \brief An array of light-weight synchronisation
303          *  primitives (spinlocks).
304          *
305          * \details In this implementation, storage is optimized so that
306          * a single bit per spinlock is used.
307          *
308          */
309         class SpinLockArray {
310         public:
311             /**
312              * \brief Internal representation of SpinLockArray elements.
313              * \details Each word_t represents 32 spinlocks.
314              */
315             typedef Numeric::uint32 word_t;
316 
317             /**
318              * \brief Constructs a new SpinLockArray of size 0.
319              */
SpinLockArray()320             SpinLockArray() : size_(0) {
321             }
322 
323             /**
324              * \brief Constructs a new SpinLockArray of size \p size_in.
325              * \param[in] size_in number of spinlocks in the array.
326              */
SpinLockArray(index_t size_in)327             SpinLockArray(index_t size_in) : size_(0) {
328                 resize(size_in);
329             }
330 
331             /**
332              * \brief Resizes a SpinLockArray.
333              * \details All the spinlocks are reset to 0.
334              * \param[in] size_in The desired new size.
335              */
resize(index_t size_in)336             void resize(index_t size_in) {
337                 if(size_ != size_in) {
338                     size_ = size_in;
339                     index_t nb_words = (size_ >> 5) + 1;
340                     spinlocks_.assign(nb_words, 0);
341                 }
342             }
343 
344             /**
345              * \brief Gets the number of spinlocks in this array.
346              */
size()347             index_t size() const {
348                 return size_;
349             }
350 
351             /**
352              * \brief Resets size to 0 and clears all the memory.
353              */
clear()354             void clear() {
355                 spinlocks_.clear();
356             }
357 
358             /**
359              * \brief Acquires a spinlock at a given index
360              * \details Loops until spinlock at index \p i is available then
361              * reserve it.
362              * \param[in] i index of the spinlock
363              */
acquire_spinlock(index_t i)364             void acquire_spinlock(index_t i) {
365                 geo_thread_sync_assert(i < size());
366                 index_t w = i >> 5;
367                 word_t b = word_t(i & 31);
368                 // Loop while previously stored value has its bit set.
369                 while((atomic_bitset_arm32(&spinlocks_[w], b)) != 0) {
370                     // If somebody else has the lock, sleep.
371                     //  It is important to sleep here, else atomic_bitset_xxx()
372                     // keeps acquiring the exclusive monitor (even for testing)
373                     // and this slows down everything.
374                     wait_for_event_arm32();
375                 }
376                 memory_barrier_arm32();
377             }
378 
379             /**
380              * \brief Releases a spinlock at a given index
381              * \details Makes spinlock at index \p i available to other threads.
382              * \param[in] i index of the spinlock
383              */
release_spinlock(index_t i)384             void release_spinlock(index_t i) {
385                 geo_thread_sync_assert(i < size());
386                 memory_barrier_android();
387                 index_t w = i >> 5;
388                 word_t b = word_t(i & 31);
389                 atomic_bitreset_arm32(&spinlocks_[w], b);
390                 //   Now wake up the other threads that started
391                 // sleeping if they did not manage to acquire
392                 // the lock.
393                 send_event_arm32();
394             }
395 
396         private:
397             std::vector<word_t> spinlocks_;
398             index_t size_;
399         };
400 
401 #elif defined(GEO_OS_ANDROID)
402 
403         /**
404          * \brief An array of light-weight synchronisation
405          *  primitives (spinlocks).
406          *
407          * \details In this implementation, storage is optimized so that
408          * a single bit per spinlock is used.
409          *
410          */
411         class SpinLockArray {
412         public:
413             /**
414              * \brief Internal representation of SpinLockArray elements.
415              * \details Each word_t represents 32 spinlocks.
416              */
417             typedef Numeric::uint32 word_t;
418 
419             /**
420              * \brief Constructs a new SpinLockArray of size 0.
421              */
SpinLockArray()422             SpinLockArray() : size_(0) {
423             }
424 
425             /**
426              * \brief Constructs a new SpinLockArray of size \p size_in.
427              * \param[in] size_in number of spinlocks in the array.
428              */
SpinLockArray(index_t size_in)429             SpinLockArray(index_t size_in) : size_(0) {
430                 resize(size_in);
431             }
432 
433             /**
434              * \brief Resizes a SpinLockArray.
435              * \details All the spinlocks are reset to 0.
436              * \param[in] size_in The desired new size.
437              */
resize(index_t size_in)438             void resize(index_t size_in) {
439                 if(size_ != size_in) {
440                     size_ = size_in;
441                     index_t nb_words = (size_ >> 5) + 1;
442                     spinlocks_.assign(nb_words, 0);
443                 }
444             }
445 
446             /**
447              * \brief Gets the number of spinlocks in this array.
448              */
size()449             index_t size() const {
450                 return size_;
451             }
452 
453             /**
454              * \brief Resets size to 0 and clears all the memory.
455              */
clear()456             void clear() {
457                 spinlocks_.clear();
458             }
459 
460             /**
461              * \brief Acquires a spinlock at a given index
462              * \details Loops until spinlock at index \p i is available then
463              * reserve it.
464              * \param[in] i index of the spinlock
465              */
acquire_spinlock(index_t i)466             void acquire_spinlock(index_t i) {
467                 geo_thread_sync_assert(i < size());
468                 index_t w = i >> 5;
469                 word_t b = word_t(i & 31);
470                 // Loop while previously stored value has its bit set.
471                 while((atomic_bitset_android(&spinlocks_[w], b)) != 0) {
472                     // If somebody else has the lock, sleep.
473                     //  It is important to sleep here, else atomic_bitset_xxx()
474                     // keeps acquiring the exclusive monitor (even for testing)
475                     // and this slows down everything.
476                     wait_for_event_android();
477                 }
478                 memory_barrier_android();
479             }
480 
481             /**
482              * \brief Releases a spinlock at a given index
483              * \details Makes spinlock at index \p i available to other threads.
484              * \param[in] i index of the spinlock
485              */
release_spinlock(index_t i)486             void release_spinlock(index_t i) {
487                 geo_thread_sync_assert(i < size());
488                 memory_barrier_android();
489                 index_t w = i >> 5;
490                 word_t b = word_t(i & 31);
491                 atomic_bitreset_android(&spinlocks_[w], b);
492                 //   Now wake up the other threads that started
493                 // sleeping if they did not manage to acquire
494                 // the lock.
495                 send_event_android();
496             }
497 
498         private:
499             std::vector<word_t> spinlocks_;
500             index_t size_;
501         };
502 
503 #elif defined(GEO_OS_LINUX) || defined(GEO_OS_DRAGONFLY)
504 
505         /**
506          * \brief An array of light-weight synchronisation
507          *  primitives (spinlocks).
508          *
509          * \details In this implementation, storage is optimized so that
510          * a single bit per spinlock is used.
511          *
512          * \see acquire_spinlock(), release_spinlock()
513          */
514         class SpinLockArray {
515         public:
516             /**
517              * \brief Internal representation of SpinLockArray elements.
518              * \details Each word_t represents 32 spinlocks.
519              */
520             typedef Numeric::uint32 word_t;
521 
522             /**
523              * \brief Constructs a new SpinLockArray of size 0.
524              */
SpinLockArray()525             SpinLockArray() : size_(0) {
526             }
527 
528             /**
529              * \brief Constructs a new SpinLockArray of size \p size_in.
530              * \param[in] size_in number of spinlocks in the array.
531              */
SpinLockArray(index_t size_in)532             SpinLockArray(index_t size_in) : size_(0) {
533                 resize(size_in);
534             }
535 
536             /**
537              * \brief Resizes a SpinLockArray.
538              * \details All the spinlocks are reset to 0.
539              * \param[in] size_in The desired new size.
540              */
resize(index_t size_in)541             void resize(index_t size_in) {
542                 if(size_ != size_in) {
543                     size_ = size_in;
544                     index_t nb_words = (size_ >> 5) + 1;
545                     spinlocks_.assign(nb_words, 0);
546                 }
547             }
548 
549             /**
550              * \brief Gets the number of spinlocks in this array.
551              */
size()552             index_t size() const {
553                 return size_;
554             }
555 
556             /**
557              * \brief Resets size to 0 and clears all the memory.
558              */
clear()559             void clear() {
560                 spinlocks_.clear();
561             }
562 
563             /**
564              * \brief Acquires a spinlock at a given index
565              * \details Loops until spinlock at index \p i is available then
566              * reserve it.
567              * \param[in] i index of the spinlock
568              */
acquire_spinlock(index_t i)569             void acquire_spinlock(index_t i) {
570                 geo_thread_sync_assert(i < size());
571                 index_t w = i >> 5;
572                 index_t b = i & 31;
573                 while(atomic_bittestandset_x86(&spinlocks_[w], Numeric::uint32(b))) {
574                     // Intel recommends to have a PAUSE asm instruction
575                     // in the spinlock loop. It is generated using the
576                     // following intrinsic function of GCC.
577                     geo_pause();
578                 }
579             }
580 
581             /**
582              * \brief Releases a spinlock at a given index
583              * \details Makes spinlock at index \p i available to other threads.
584              * \param[in] i index of the spinlock
585              */
release_spinlock(index_t i)586             void release_spinlock(index_t i) {
587                 geo_thread_sync_assert(i < size());
588                 index_t w = i >> 5;
589                 index_t b = i & 31;
590                 // Note: we need here to use a synchronized bit reset
591                 // since &= is not atomic.
592                 atomic_bittestandreset_x86(&spinlocks_[w], Numeric::uint32(b));
593             }
594 
595         private:
596             std::vector<word_t> spinlocks_;
597             index_t size_;
598         };
599 
600 #elif defined(GEO_OS_WINDOWS)
601         /**
602          * \brief An array of light-weight synchronisation
603          *  primitives (spinlocks).
604          *
605          * \details In this implementation, storage is optimized so that
606          * a single bit per spinlock is used.
607          *
608          * \see acquire_spinlock(), release_spinlock()
609          */
610         class SpinLockArray {
611         public:
612             /**
613              * \brief Internal representation of SpinLockArray elements.
614              * \details Each word_t represents 32 spinlocks.
615              * \internal
616              * LONG is 32 bits under MSVC
617              * and is what interlockedbittestand(re)set uses.
618              */
619             typedef LONG word_t;
620 
621             /**
622              * \brief Constructs a new SpinLockArray of size 0.
623              */
SpinLockArray()624             SpinLockArray() : size_(0) {
625             }
626 
627             /**
628              * \brief Constructs a new SpinLockArray of size \p size_in.
629              * \param[in] size_in number of spinlocks in the array.
630              */
SpinLockArray(index_t size_in)631             SpinLockArray(index_t size_in) : size_(0) {
632                 resize(size_in);
633             }
634 
635             /**
636              * \brief Resizes a SpinLockArray.
637              * \details All the spinlocks are reset to 0.
638              * \param[in] size_in The desired new size.
639              */
resize(index_t size_in)640             void resize(index_t size_in) {
641                 if(size_ != size_in) {
642                     size_ = size_in;
643                     index_t nb_words = (size_ >> 5) + 1;
644                     spinlocks_.assign(nb_words, 0);
645                 }
646             }
647 
648             /**
649              * \brief Gets the number of spinlocks in this array.
650              */
size()651             index_t size() const {
652                 return size_;
653             }
654 
655             /**
656              * \brief Resets size to 0 and clears all the memory.
657              */
clear()658             void clear() {
659                 spinlocks_.clear();
660             }
661 
662             /**
663              * \brief Acquires a spinlock at a given index
664              * \details Loops until spinlock at index \p i is available then
665              * reserve it.
666              * \param[in] i index of the spinlock
667              */
acquire_spinlock(index_t i)668             void acquire_spinlock(index_t i) {
669                 geo_thread_sync_assert(i < size());
670                 index_t w = i >> 5;
671                 index_t b = i & 31;
672                 while(_interlockedbittestandset((long *)(&spinlocks_[w]), long(b))) {
673                     // Intel recommends to have a PAUSE asm instruction
674                     // in the spinlock loop. Under MSVC/Windows,
675                     // YieldProcessor() is a macro that calls the
676                     // (undocumented) _mm_pause() intrinsic function
677                     // that generates a PAUSE opcode.
678                     YieldProcessor();
679                 }
680                 // We do not need here _ReadBarrier() since
681                 // _interlockedbittestandset
682                 // "acts as a full barrier in VC2005" according to the doc
683             }
684 
685             /**
686              * \brief Releases a spinlock at a given index
687              * \details Makes spinlock at index \p i available to other threads.
688              * \param[in] i index of the spinlock
689              */
release_spinlock(index_t i)690             void release_spinlock(index_t i) {
691                 geo_thread_sync_assert(i < size());
692                 index_t w = i >> 5;
693                 index_t b = i & 31;
694                 // Note1: we need here to use a synchronized bit reset
695                 // since |= is not atomic.
696                 // Note2: We do not need here _WriteBarrier() since
697                 // _interlockedbittestandreset
698                 // "acts as a full barrier in VC2005" according to the doc
699                 _interlockedbittestandreset((long*)(&spinlocks_[w]), long(b));
700             }
701 
702         private:
703             std::vector<word_t> spinlocks_;
704             index_t size_;
705         };
706 
707 #else
708 
709 #error Found no implementation of SpinLockArray
710 
711 #endif
712 
713 
714     }
715 
716 #ifdef GEO_OS_WINDOWS
717 
718     // Emulation of pthread mutexes using Windows API
719 
720     typedef CRITICAL_SECTION pthread_mutex_t;
721     typedef unsigned int pthread_mutexattr_t;
722 
pthread_mutex_lock(pthread_mutex_t * m)723     inline int pthread_mutex_lock(pthread_mutex_t *m) {
724         EnterCriticalSection(m);
725         return 0;
726     }
727 
pthread_mutex_unlock(pthread_mutex_t * m)728     inline int pthread_mutex_unlock(pthread_mutex_t *m) {
729         LeaveCriticalSection(m);
730         return 0;
731     }
732 
pthread_mutex_trylock(pthread_mutex_t * m)733     inline int pthread_mutex_trylock(pthread_mutex_t *m) {
734         return TryEnterCriticalSection(m) ? 0 : EBUSY;
735     }
736 
pthread_mutex_init(pthread_mutex_t * m,pthread_mutexattr_t * a)737     inline int pthread_mutex_init(pthread_mutex_t *m, pthread_mutexattr_t *a) {
738         geo_argused(a);
739         InitializeCriticalSection(m);
740         return 0;
741     }
742 
pthread_mutex_destroy(pthread_mutex_t * m)743     inline int pthread_mutex_destroy(pthread_mutex_t *m) {
744         DeleteCriticalSection(m);
745         return 0;
746     }
747 
748 
749     // Emulation of pthread condition variables using Windows API
750 
751     typedef CONDITION_VARIABLE pthread_cond_t;
752     typedef unsigned int pthread_condattr_t;
753 
pthread_cond_init(pthread_cond_t * c,pthread_condattr_t * a)754     inline int pthread_cond_init(pthread_cond_t *c, pthread_condattr_t *a) {
755         geo_argused(a);
756         InitializeConditionVariable(c);
757         return 0;
758     }
759 
pthread_cond_destroy(pthread_cond_t * c)760     inline int pthread_cond_destroy(pthread_cond_t *c) {
761         geo_argused(c);
762         return 0;
763     }
764 
pthread_cond_broadcast(pthread_cond_t * c)765     inline int pthread_cond_broadcast(pthread_cond_t *c) {
766         WakeAllConditionVariable(c);
767         return 0;
768     }
769 
pthread_cond_wait(pthread_cond_t * c,pthread_mutex_t * m)770     inline int pthread_cond_wait(pthread_cond_t *c, pthread_mutex_t *m) {
771         SleepConditionVariableCS(c, m, INFINITE);
772         return 0;
773     }
774 
775 #endif
776 
777 }
778 
779 #endif
780 
781