mudgangster

Tiny, scriptable MUD client
Log | Files | Refs | README

tracy_sema.h (6192B)


      1 // Copyright (c) 2015 Jeff Preshing
      2 //
      3 // This software is provided 'as-is', without any express or implied
      4 // warranty. In no event will the authors be held liable for any damages
      5 // arising from the use of this software.
      6 //
      7 // Permission is granted to anyone to use this software for any purpose,
      8 // including commercial applications, and to alter it and redistribute it
      9 // freely, subject to the following restrictions:
     10 //
     11 // 1. The origin of this software must not be misrepresented; you must not
     12 // claim that you wrote the original software. If you use this software
     13 // in a product, an acknowledgement in the product documentation would be
     14 // appreciated but is not required.
     15 // 2. Altered source versions must be plainly marked as such, and must not be
     16 // misrepresented as being the original software.
     17 // 3. This notice may not be removed or altered from any source distribution.
     18 
     19 #ifndef __TRACY_CPP11OM_SEMAPHORE_H__
     20 #define __TRACY_CPP11OM_SEMAPHORE_H__
     21 
     22 #include <atomic>
     23 #include <cassert>
     24 
     25 #if defined(__MACH__)
     26     #include <mach/mach.h>
     27 #elif defined(__unix__)
     28     #include <semaphore.h>
     29 #endif
     30 
     31 namespace tracy
     32 {
     33 
     34 #if defined(_WIN32)
     35 //---------------------------------------------------------
     36 // Semaphore (Windows)
     37 //---------------------------------------------------------
     38 #ifndef MAXLONG
     39 enum { MAXLONG = 0x7fffffff };
     40 #endif
     41 
     42 #ifndef INFINITE
     43 enum { INFINITE = 0xFFFFFFFF };
     44 #endif
     45 
     46 #ifndef _WINDOWS_
     47 typedef void* HANDLE;
     48 
     49 extern "C" __declspec(dllimport) HANDLE __stdcall CreateSemaphoreA( void*, long, long, const char* );
     50 extern "C" __declspec(dllimport) int __stdcall CloseHandle( HANDLE );
     51 extern "C" __declspec(dllimport) unsigned long __stdcall WaitForSingleObject( HANDLE, unsigned long );
     52 extern "C" __declspec(dllimport) int __stdcall ReleaseSemaphore( HANDLE, long, long* );
     53 #endif
     54 
     55 class Semaphore
     56 {
     57 private:
     58     HANDLE m_hSema;
     59 
     60     Semaphore(const Semaphore& other) = delete;
     61     Semaphore& operator=(const Semaphore& other) = delete;
     62 
     63 public:
     64     Semaphore(int initialCount = 0)
     65     {
     66         assert(initialCount >= 0);
     67         m_hSema = CreateSemaphoreA(NULL, initialCount, MAXLONG, NULL);
     68     }
     69 
     70     ~Semaphore()
     71     {
     72         CloseHandle(m_hSema);
     73     }
     74 
     75     void wait()
     76     {
     77         WaitForSingleObject(m_hSema, INFINITE);
     78     }
     79 
     80     void signal(int count = 1)
     81     {
     82         ReleaseSemaphore(m_hSema, count, NULL);
     83     }
     84 };
     85 
     86 
     87 #elif defined(__MACH__)
     88 //---------------------------------------------------------
     89 // Semaphore (Apple iOS and OSX)
     90 // Can't use POSIX semaphores due to http://lists.apple.com/archives/darwin-kernel/2009/Apr/msg00010.html
     91 //---------------------------------------------------------
     92 
     93 class Semaphore
     94 {
     95 private:
     96     semaphore_t m_sema;
     97 
     98     Semaphore(const Semaphore& other) = delete;
     99     Semaphore& operator=(const Semaphore& other) = delete;
    100 
    101 public:
    102     Semaphore(int initialCount = 0)
    103     {
    104         assert(initialCount >= 0);
    105         semaphore_create(mach_task_self(), &m_sema, SYNC_POLICY_FIFO, initialCount);
    106     }
    107 
    108     ~Semaphore()
    109     {
    110         semaphore_destroy(mach_task_self(), m_sema);
    111     }
    112 
    113     void wait()
    114     {
    115         semaphore_wait(m_sema);
    116     }
    117 
    118     void signal()
    119     {
    120         semaphore_signal(m_sema);
    121     }
    122 
    123     void signal(int count)
    124     {
    125         while (count-- > 0)
    126         {
    127             semaphore_signal(m_sema);
    128         }
    129     }
    130 };
    131 
    132 
    133 #elif defined(__unix__)
    134 //---------------------------------------------------------
    135 // Semaphore (POSIX, Linux)
    136 //---------------------------------------------------------
    137 
    138 class Semaphore
    139 {
    140 private:
    141     sem_t m_sema;
    142 
    143     Semaphore(const Semaphore& other) = delete;
    144     Semaphore& operator=(const Semaphore& other) = delete;
    145 
    146 public:
    147     Semaphore(int initialCount = 0)
    148     {
    149         assert(initialCount >= 0);
    150         sem_init(&m_sema, 0, initialCount);
    151     }
    152 
    153     ~Semaphore()
    154     {
    155         sem_destroy(&m_sema);
    156     }
    157 
    158     void wait()
    159     {
    160         // http://stackoverflow.com/questions/2013181/gdb-causes-sem-wait-to-fail-with-eintr-error
    161         int rc;
    162         do
    163         {
    164             rc = sem_wait(&m_sema);
    165         }
    166         while (rc == -1 && errno == EINTR);
    167     }
    168 
    169     void signal()
    170     {
    171         sem_post(&m_sema);
    172     }
    173 
    174     void signal(int count)
    175     {
    176         while (count-- > 0)
    177         {
    178             sem_post(&m_sema);
    179         }
    180     }
    181 };
    182 
    183 
    184 #else
    185 
    186 #error Unsupported platform!
    187 
    188 #endif
    189 
    190 
    191 //---------------------------------------------------------
    192 // LightweightSemaphore
    193 //---------------------------------------------------------
    194 class LightweightSemaphore
    195 {
    196 private:
    197     std::atomic<int> m_count;
    198     Semaphore m_sema;
    199 
    200     void waitWithPartialSpinning()
    201     {
    202         int oldCount;
    203         // Is there a better way to set the initial spin count?
    204         // If we lower it to 1000, testBenaphore becomes 15x slower on my Core i7-5930K Windows PC,
    205         // as threads start hitting the kernel semaphore.
    206         int spin = 10000;
    207         while (spin--)
    208         {
    209             oldCount = m_count.load(std::memory_order_relaxed);
    210             if ((oldCount > 0) && m_count.compare_exchange_strong(oldCount, oldCount - 1, std::memory_order_acquire))
    211                 return;
    212             std::atomic_signal_fence(std::memory_order_acquire);     // Prevent the compiler from collapsing the loop.
    213         }
    214         oldCount = m_count.fetch_sub(1, std::memory_order_acquire);
    215         if (oldCount <= 0)
    216         {
    217             m_sema.wait();
    218         }
    219     }
    220 
    221 public:
    222     LightweightSemaphore(int initialCount = 0) : m_count(initialCount)
    223     {
    224         assert(initialCount >= 0);
    225     }
    226 
    227     bool tryWait()
    228     {
    229         int oldCount = m_count.load(std::memory_order_relaxed);
    230         return (oldCount > 0 && m_count.compare_exchange_strong(oldCount, oldCount - 1, std::memory_order_acquire));
    231     }
    232 
    233     void wait()
    234     {
    235         if (!tryWait())
    236             waitWithPartialSpinning();
    237     }
    238 
    239     void signal(int count = 1)
    240     {
    241         int oldCount = m_count.fetch_add(count, std::memory_order_release);
    242         int toRelease = -oldCount < count ? -oldCount : count;
    243         if (toRelease > 0)
    244         {
    245             m_sema.signal(toRelease);
    246         }
    247     }
    248 };
    249 
    250 
    251 typedef LightweightSemaphore DefaultSemaphoreType;
    252 
    253 }
    254 
    255 #endif // __CPP11OM_SEMAPHORE_H__