tc_sem_mutex.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. /**
  2. * Tencent is pleased to support the open source community by making Tars available.
  3. *
  4. * Copyright (C) 2016THL A29 Limited, a Tencent company. All rights reserved.
  5. *
  6. * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
  7. * in compliance with the License. You may obtain a copy of the License at
  8. *
  9. * https://opensource.org/licenses/BSD-3-Clause
  10. *
  11. * Unless required by applicable law or agreed to in writing, software distributed
  12. * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
  13. * CONDITIONS OF ANY KIND, either express or implied. See the License for the
  14. * specific language governing permissions and limitations under the License.
  15. */
  16. #include <errno.h>
  17. #include <string.h>
  18. #include "util/tc_common.h"
  19. #include "util/tc_sem_mutex.h"
  20. namespace tars
  21. {
  22. #if TARGET_PLATFORM_LINUX || TARGET_PLATFORM_IOS
  23. TC_SemMutex::TC_SemMutex():_semID(-1),_semKey(-1)
  24. {
  25. }
  26. TC_SemMutex::TC_SemMutex(key_t iKey)
  27. {
  28. init(iKey);
  29. }
  30. TC_SemMutex::~TC_SemMutex()
  31. {
  32. }
  33. void TC_SemMutex::init(key_t iKey)
  34. {
  35. #if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
  36. /* union semun is defined by including <sys/sem.h> */
  37. #else
  38. /* according to X/OPEN we have to define it ourselves */
  39. union semun
  40. {
  41. int val; /* value for SETVAL */
  42. struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */
  43. unsigned short *array; /* array for GETALL, SETALL */
  44. /* Linux specific part: */
  45. struct seminfo *__buf; /* buffer for IPC_INFO */
  46. };
  47. #endif
  48. int iSemID;
  49. union semun arg;
  50. u_short array[2] = { 0, 0 };
  51. //生成信号量集, 包含两个信号量
  52. if ( (iSemID = semget( iKey, 2, IPC_CREAT | IPC_EXCL | 0666)) != -1 )
  53. {
  54. arg.array = &array[0];
  55. //将所有信号量的值设置为0
  56. if ( semctl( iSemID, 0, SETALL, arg ) == -1 )
  57. {
  58. THROW_EXCEPTION_SYSCODE(TC_SemMutex_Exception, "[TC_SemMutex::init] semctl error");
  59. // throw TC_SemMutex_Exception("[TC_SemMutex::init] semctl error", TC_Exception::getSystemCode());
  60. }
  61. }
  62. else
  63. {
  64. //信号量已经存在
  65. if ( errno != EEXIST )
  66. {
  67. THROW_EXCEPTION_SYSCODE(TC_SemMutex_Exception, "[TC_SemMutex::init] sem has exist error");
  68. // throw TC_SemMutex_Exception("[TC_SemMutex::init] sem has exist error", TC_Exception::getSystemCode());
  69. }
  70. //连接信号量
  71. if ( (iSemID = semget( iKey, 2, 0666 )) == -1 )
  72. {
  73. THROW_EXCEPTION_SYSCODE(TC_SemMutex_Exception, "[TC_SemMutex::init] connect sem error");
  74. // throw TC_SemMutex_Exception("[TC_SemMutex::init] connect sem error", TC_Exception::getSystemCode());
  75. }
  76. }
  77. _semKey = iKey;
  78. _semID = iSemID;
  79. }
  80. void TC_SemMutex::rlock() const
  81. {
  82. //进入共享锁, 第二个信号量的值表示当前使用信号量的进程个数
  83. //等待第一个信号量变为0(排他锁没有使用)
  84. //占用第二个信号量(第二个信号量值+1, 表示被共享锁使用)
  85. struct sembuf sops[2] = { {0, 0, SEM_UNDO}, {1, 1, SEM_UNDO} };
  86. size_t nsops = 2;
  87. int ret = -1;
  88. do
  89. {
  90. ret=semop(_semID,&sops[0],nsops);
  91. } while ((ret == -1) &&(errno==EINTR));
  92. // return ret;
  93. //return semop( _semID, &sops[0], nsops);
  94. }
  95. void TC_SemMutex::unrlock( ) const
  96. {
  97. //解除共享锁, 有进程使用过第二个信号量
  98. //等到第二个信号量可以使用(第二个信号量的值>=1)
  99. struct sembuf sops[1] = { {1, -1, SEM_UNDO} };
  100. size_t nsops = 1;
  101. int ret = -1;
  102. do
  103. {
  104. ret=semop(_semID,&sops[0],nsops);
  105. } while ((ret == -1) &&(errno==EINTR));
  106. // return ret;
  107. //return semop( _semID, &sops[0], nsops);
  108. }
  109. bool TC_SemMutex::tryrlock() const
  110. {
  111. struct sembuf sops[2] = { {0, 0, SEM_UNDO|IPC_NOWAIT}, {1, 1, SEM_UNDO|IPC_NOWAIT}};
  112. size_t nsops = 2;
  113. int iRet = semop( _semID, &sops[0], nsops );
  114. if(iRet == -1)
  115. {
  116. if(errno == EAGAIN)
  117. {
  118. //无法获得锁
  119. return false;
  120. }
  121. else
  122. {
  123. THROW_EXCEPTION_SYSCODE(TC_SemMutex_Exception, "[TC_SemMutex::tryrlock] semop error");
  124. // throw TC_SemMutex_Exception("[TC_SemMutex::tryrlock] semop error", TC_Exception::getSystemCode());
  125. }
  126. }
  127. return true;
  128. }
  129. void TC_SemMutex::wlock() const
  130. {
  131. //进入排他锁, 第一个信号量和第二个信号都没有被使用过(即, 两个锁都没有被使用)
  132. //等待第一个信号量变为0
  133. //等待第二个信号量变为0
  134. //释放第一个信号量(第一个信号量+1, 表示有一个进程使用第一个信号量)
  135. struct sembuf sops[3] = { {0, 0, SEM_UNDO}, {1, 0, SEM_UNDO}, {0, 1, SEM_UNDO} };
  136. size_t nsops = 3;
  137. int ret = -1;
  138. do
  139. {
  140. ret=semop(_semID,&sops[0],nsops);
  141. } while ((ret == -1) &&(errno==EINTR));
  142. // return ret;
  143. //return semop( _semID, &sops[0], nsops);
  144. }
  145. void TC_SemMutex::unwlock() const
  146. {
  147. //解除排他锁, 有进程使用过第一个信号量
  148. //等待第一个信号量(信号量值>=1)
  149. struct sembuf sops[1] = { {0, -1, SEM_UNDO} };
  150. size_t nsops = 1;
  151. int ret = -1;
  152. do
  153. {
  154. ret=semop(_semID,&sops[0],nsops);
  155. } while ((ret == -1) &&(errno==EINTR));
  156. // return ret;
  157. //return semop( _semID, &sops[0], nsops);
  158. }
  159. bool TC_SemMutex::trywlock() const
  160. {
  161. struct sembuf sops[3] = { {0, 0, SEM_UNDO|IPC_NOWAIT}, {1, 0, SEM_UNDO|IPC_NOWAIT}, {0, 1, SEM_UNDO|IPC_NOWAIT} };
  162. size_t nsops = 3;
  163. int iRet = semop( _semID, &sops[0], nsops );
  164. if(iRet == -1)
  165. {
  166. if(errno == EAGAIN)
  167. {
  168. //无法获得锁
  169. return false;
  170. }
  171. else
  172. {
  173. THROW_EXCEPTION_SYSCODE(TC_SemMutex_Exception, "[TC_SemMutex::trywlock] semop error");
  174. // throw TC_SemMutex_Exception("[TC_SemMutex::trywlock] semop error", TC_Exception::getSystemCode());
  175. }
  176. }
  177. return true;
  178. }
  179. #else
  180. TC_SemMutex::TC_SemMutex():_readers(0), _writersWaiting(0), _writers(0)
  181. {
  182. }
  183. TC_SemMutex::TC_SemMutex(key_t iKey)
  184. {
  185. init(iKey);
  186. }
  187. TC_SemMutex::~TC_SemMutex()
  188. {
  189. if(_mutex != NULL)
  190. {
  191. CloseHandle(_mutex);
  192. _mutex = NULL;
  193. }
  194. if(_readEvent!= NULL)
  195. {
  196. CloseHandle(_readEvent);
  197. _readEvent = NULL;
  198. }
  199. if(_writeEvent != NULL)
  200. {
  201. CloseHandle(_writeEvent);
  202. _writeEvent = NULL;
  203. }
  204. }
  205. void TC_SemMutex::init(key_t iKey)
  206. {
  207. string key = "tc-mutex-" + TC_Common::tostr(iKey);
  208. string rkey = "tc-readEvent-" + TC_Common::tostr(iKey);
  209. string wkey = "tc-writeEvent-" + TC_Common::tostr(iKey);
  210. _mutex = CreateMutex(NULL, FALSE, key.c_str());
  211. if (_mutex == NULL)
  212. {
  213. THROW_EXCEPTION_SYSCODE(TC_SemMutex_Exception, "[TC_SemMutex::init] CreateMutex error");
  214. }
  215. _readEvent = CreateEvent(NULL, TRUE, TRUE, rkey.c_str());
  216. if (_readEvent == NULL)
  217. {
  218. CloseHandle(_mutex);
  219. _mutex = NULL;
  220. THROW_EXCEPTION_SYSCODE(TC_SemMutex_Exception, "[TC_SemMutex::init] CreateEvent error");
  221. }
  222. _writeEvent = CreateEvent(NULL, TRUE, TRUE, wkey.c_str());
  223. if (_writeEvent == NULL)
  224. {
  225. CloseHandle(_mutex);
  226. _mutex = NULL;
  227. CloseHandle(_readEvent);
  228. _readEvent = NULL;
  229. THROW_EXCEPTION_SYSCODE(TC_SemMutex_Exception, "[TC_SemMutex::init] CreateEvent error");
  230. }
  231. _semKey = iKey;
  232. }
  233. void TC_SemMutex::addWriter() const
  234. {
  235. switch (WaitForSingleObject(_mutex, INFINITE))
  236. {
  237. case WAIT_OBJECT_0:
  238. if (++_writersWaiting == 1)
  239. ResetEvent(_readEvent);
  240. ReleaseMutex(_mutex);
  241. break;
  242. default:
  243. THROW_EXCEPTION_SYSCODE(TC_SemMutex_Exception, "[TC_SemMutex::addWriter] WaitForSingleObject error");
  244. }
  245. }
  246. void TC_SemMutex::removeWriter() const
  247. {
  248. switch (WaitForSingleObject(_mutex, INFINITE))
  249. {
  250. case WAIT_OBJECT_0:
  251. if (--_writersWaiting == 0 && _writers == 0)
  252. SetEvent(_readEvent);
  253. ReleaseMutex(_mutex);
  254. break;
  255. default:
  256. THROW_EXCEPTION_SYSCODE(TC_SemMutex_Exception, "[TC_SemMutex::removeWriter] WaitForSingleObject error");
  257. }
  258. }
  259. void TC_SemMutex::rlock() const
  260. {
  261. HANDLE h[2];
  262. h[0] = _mutex;
  263. h[1] = _readEvent;
  264. switch (WaitForMultipleObjects(2, h, TRUE, INFINITE))
  265. {
  266. case WAIT_OBJECT_0:
  267. case WAIT_OBJECT_0 + 1:
  268. ++_readers;
  269. ResetEvent(_writeEvent);
  270. ReleaseMutex(_mutex);
  271. assert(_writers == 0);
  272. break;
  273. default:
  274. THROW_EXCEPTION_SYSCODE(TC_SemMutex_Exception, "[TC_SemMutex::rlock] WaitForSingleObject error");
  275. }
  276. }
  277. void TC_SemMutex::unrlock( ) const
  278. {
  279. unlockImp();
  280. }
  281. bool TC_SemMutex::tryrlock() const
  282. {
  283. for (;;)
  284. {
  285. if (_writers != 0 || _writersWaiting != 0)
  286. return false;
  287. DWORD result = tryReadLockOnce();
  288. switch (result)
  289. {
  290. case WAIT_OBJECT_0:
  291. case WAIT_OBJECT_0 + 1:
  292. return true;
  293. case WAIT_TIMEOUT:
  294. continue;
  295. default:
  296. THROW_EXCEPTION_SYSCODE(TC_SemMutex_Exception, "[TC_SemMutex::tryrlock] WaitForSingleObject error");
  297. }
  298. }
  299. }
  300. void TC_SemMutex::wlock() const
  301. {
  302. addWriter();
  303. HANDLE h[2];
  304. h[0] = _mutex;
  305. h[1] = _writeEvent;
  306. switch (WaitForMultipleObjects(2, h, TRUE, INFINITE))
  307. {
  308. case WAIT_OBJECT_0:
  309. case WAIT_OBJECT_0 + 1:
  310. --_writersWaiting;
  311. ++_readers;
  312. ++_writers;
  313. ResetEvent(_readEvent);
  314. ResetEvent(_writeEvent);
  315. ReleaseMutex(_mutex);
  316. assert(_writers == 1);
  317. break;
  318. default:
  319. removeWriter();
  320. THROW_EXCEPTION_SYSCODE(TC_SemMutex_Exception, "[TC_SemMutex::wlock] WaitForSingleObject error");
  321. }
  322. }
  323. void TC_SemMutex::unwlock() const
  324. {
  325. unlockImp();
  326. }
  327. bool TC_SemMutex::trywlock() const
  328. {
  329. addWriter();
  330. HANDLE h[2];
  331. h[0] = _mutex;
  332. h[1] = _writeEvent;
  333. switch (WaitForMultipleObjects(2, h, TRUE, 1))
  334. {
  335. case WAIT_OBJECT_0:
  336. case WAIT_OBJECT_0 + 1:
  337. --_writersWaiting;
  338. ++_readers;
  339. ++_writers;
  340. ResetEvent(_readEvent);
  341. ResetEvent(_writeEvent);
  342. ReleaseMutex(_mutex);
  343. assert(_writers == 1);
  344. return true;
  345. case WAIT_TIMEOUT:
  346. default:
  347. removeWriter();
  348. }
  349. return false;
  350. }
  351. void TC_SemMutex::unlockImp() const
  352. {
  353. switch (WaitForSingleObject(_mutex, INFINITE))
  354. {
  355. case WAIT_OBJECT_0:
  356. _writers = 0;
  357. if (_writersWaiting == 0) SetEvent(_readEvent);
  358. if (--_readers == 0) SetEvent(_writeEvent);
  359. ReleaseMutex(_mutex);
  360. break;
  361. default:
  362. THROW_EXCEPTION_SYSCODE(TC_SemMutex_Exception, "[TC_SemMutex::unlockImp] WaitForSingleObject error");
  363. }
  364. }
  365. DWORD TC_SemMutex::tryReadLockOnce() const
  366. {
  367. HANDLE h[2];
  368. h[0] = _mutex;
  369. h[1] = _readEvent;
  370. DWORD result = WaitForMultipleObjects(2, h, TRUE, 1);
  371. switch (result)
  372. {
  373. case WAIT_OBJECT_0:
  374. case WAIT_OBJECT_0 + 1:
  375. ++_readers;
  376. ResetEvent(_writeEvent);
  377. ReleaseMutex(_mutex);
  378. assert(_writers == 0);
  379. return result;
  380. case WAIT_TIMEOUT:
  381. default:
  382. ;
  383. }
  384. return result;
  385. }
  386. #endif
  387. }