erlang's global lock
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

187 line
4.6 KiB

1 年之前
1 年之前
1 年之前
1 年之前
1 年之前
1 年之前
1 年之前
1 年之前
1 年之前
1 年之前
1 年之前
1 年之前
1 年之前
1 年之前
1 年之前
1 年之前
1 年之前
  1. #include "erl_nif.h"
  2. #include <atomic>
  3. using namespace std;
  4. const int LockSize = 2097152;
  5. const int HashSalt = 786234121;
  6. atomic<uint64_t> LockSlot[LockSize];
  7. ERL_NIF_TERM atomTrue;
  8. ERL_NIF_TERM atomFalse;
  9. ERL_NIF_TERM atomUndefined;
  10. typedef struct KeyNode_r
  11. {
  12. int KeyIx;
  13. struct KeyNode_r *next;
  14. } KeyNode;
  15. bool isNotLocked(KeyNode *LockedHead, int KeyIx)
  16. {
  17. KeyNode *temp = LockedHead;
  18. while (temp != NULL)
  19. {
  20. if (temp->KeyIx == KeyIx)
  21. return false;
  22. temp = temp->next;
  23. }
  24. return true;
  25. }
  26. int nifLoad(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM)
  27. {
  28. atomTrue = enif_make_atom(env, "true");
  29. atomFalse = enif_make_atom(env, "false");
  30. atomUndefined = enif_make_atom(env, "undefined");
  31. return 0;
  32. }
  33. bool lockOne(ErlNifEnv *env, ErlNifPid *ThePid, int KeyIx, uint64_t Val)
  34. {
  35. uint64_t Expected = 0;
  36. if (LockSlot[KeyIx].compare_exchange_strong(Expected, Val))
  37. {
  38. return true;
  39. }
  40. else
  41. {
  42. ThePid->pid = (ERL_NIF_TERM)Expected;
  43. if (enif_is_process_alive(env, ThePid))
  44. {
  45. return false;
  46. }
  47. else
  48. {
  49. if (LockSlot[KeyIx].compare_exchange_strong(Expected, Val))
  50. {
  51. return true;
  52. }
  53. else
  54. {
  55. return false;
  56. }
  57. }
  58. }
  59. }
  60. ERL_NIF_TERM tryLock(ErlNifEnv *env, int, const ERL_NIF_TERM argv[])
  61. {
  62. if (enif_is_list(env, argv[0]))
  63. {
  64. ERL_NIF_TERM allList = argv[0];
  65. ERL_NIF_TERM head;
  66. ErlNifPid ThePid;
  67. enif_self(env, &ThePid);
  68. uint64_t Val = (uint64_t)(ThePid.pid);
  69. int KeyIx;
  70. KeyNode *LockedHead = NULL;
  71. while (enif_get_list_cell(env, allList, &head, &allList))
  72. {
  73. KeyIx = enif_hash(ERL_NIF_INTERNAL_HASH, head, HashSalt) % LockSize;
  74. KeyNode OneKeyNode = {KeyIx, LockedHead};
  75. if (isNotLocked(LockedHead, KeyIx))
  76. {
  77. if (lockOne(env, &ThePid, KeyIx, Val))
  78. {
  79. LockedHead = &OneKeyNode;
  80. }
  81. else
  82. {
  83. uint64_t RExpected;
  84. KeyNode *temp = LockedHead;
  85. while (temp != NULL)
  86. {
  87. RExpected = Val;
  88. LockSlot[temp->KeyIx].compare_exchange_strong(RExpected, 0);
  89. temp = temp->next;
  90. }
  91. }
  92. }
  93. }
  94. return atomTrue;
  95. }
  96. else
  97. {
  98. int KeyIx;
  99. KeyIx = enif_hash(ERL_NIF_INTERNAL_HASH, argv[0], HashSalt) % LockSize;
  100. ErlNifPid ThePid;
  101. enif_self(env, &ThePid);
  102. uint64_t Val = (uint64_t)(ThePid.pid);
  103. if (lockOne(env, &ThePid, KeyIx, Val))
  104. {
  105. return atomTrue;
  106. }
  107. else
  108. {
  109. return atomFalse;
  110. }
  111. }
  112. }
  113. ERL_NIF_TERM releaseLock(ErlNifEnv *env, int, const ERL_NIF_TERM argv[])
  114. {
  115. if (enif_is_list(env, argv[0]))
  116. {
  117. ERL_NIF_TERM allList = argv[0];
  118. ERL_NIF_TERM head;
  119. ErlNifPid ThePid;
  120. enif_self(env, &ThePid);
  121. uint64_t Expected = (uint64_t)(ThePid.pid);
  122. uint64_t RExpected;
  123. int KeyIx;
  124. int isAllOk = 1;
  125. while (enif_get_list_cell(env, allList, &head, &allList))
  126. {
  127. KeyIx = enif_hash(ERL_NIF_INTERNAL_HASH, head, HashSalt) % LockSize;
  128. RExpected = Expected;
  129. if (!LockSlot[KeyIx].compare_exchange_strong(RExpected, 0))
  130. {
  131. isAllOk = 0;
  132. }
  133. }
  134. return isAllOk > 0 ? atomTrue : atomFalse;
  135. }
  136. else
  137. {
  138. int KeyIx;
  139. KeyIx = enif_hash(ERL_NIF_INTERNAL_HASH, argv[0], HashSalt) % LockSize;
  140. ErlNifPid ThePid;
  141. enif_self(env, &ThePid);
  142. uint64_t Expected = (uint64_t)(ThePid.pid);
  143. if (LockSlot[KeyIx].compare_exchange_strong(Expected, 0))
  144. {
  145. return atomTrue;
  146. }
  147. else
  148. {
  149. return atomFalse;
  150. }
  151. }
  152. }
  153. ERL_NIF_TERM getLockPid(ErlNifEnv *env, int, const ERL_NIF_TERM argv[])
  154. {
  155. int KeyIx;
  156. KeyIx = enif_hash(ERL_NIF_INTERNAL_HASH, argv[0], HashSalt) % LockSize;
  157. ErlNifPid ThePid;
  158. uint64_t Var = LockSlot[KeyIx].load();
  159. if (Var > 0)
  160. {
  161. ThePid.pid = (ERL_NIF_TERM)Var;
  162. return enif_make_pid(env, &ThePid);
  163. }
  164. else
  165. {
  166. return atomUndefined;
  167. }
  168. }
  169. static ErlNifFunc nifFuncs[] = {
  170. {"tryLock", 1, tryLock},
  171. {"releaseLock", 1, releaseLock},
  172. {"getLockPid", 1, getLockPid}};
  173. ERL_NIF_INIT(eNifLock, nifFuncs, &nifLoad, NULL, NULL, NULL)