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.

148 lines
4.3 KiB

1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
  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. int KeyIx;
  12. struct KeyNode_r *next;
  13. } KeyNode;
  14. bool isNotLocked(KeyNode *LockedHead, int KeyIx){
  15. KeyNode *temp = LockedHead;
  16. while (temp != NULL){
  17. if (temp->KeyIx == KeyIx)
  18. return false;
  19. temp = temp->next;
  20. }
  21. return true;
  22. }
  23. int nifLoad(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM){
  24. atomTrue = enif_make_atom(env, "true");
  25. atomFalse = enif_make_atom(env, "false");
  26. atomUndefined = enif_make_atom(env, "undefined");
  27. return 0;
  28. }
  29. bool lockOne(ErlNifEnv *env, ErlNifPid *ThePid, int KeyIx, uint64_t Val){
  30. uint64_t Expected = 0;
  31. if (LockSlot[KeyIx].compare_exchange_strong(Expected, Val)){
  32. return true;
  33. }else{
  34. ThePid->pid = (ERL_NIF_TERM)Expected;
  35. if (enif_is_process_alive(env, ThePid)){
  36. return false;
  37. }else{
  38. if (LockSlot[KeyIx].compare_exchange_strong(Expected, Val)){
  39. return true;
  40. }else{
  41. return false;
  42. }
  43. }
  44. }
  45. }
  46. ERL_NIF_TERM tryLock(ErlNifEnv *env, int, const ERL_NIF_TERM argv[]){
  47. if (enif_is_list(env, argv[0])){
  48. ERL_NIF_TERM allList = argv[0];
  49. ERL_NIF_TERM head;
  50. ErlNifPid ThePid;
  51. enif_self(env, &ThePid);
  52. uint64_t Val = (uint64_t)(ThePid.pid);
  53. int KeyIx;
  54. KeyNode *LockedHead = NULL;
  55. while (enif_get_list_cell(env, allList, &head, &allList)){
  56. KeyIx = enif_hash(ERL_NIF_INTERNAL_HASH, head, HashSalt) % LockSize;
  57. KeyNode OneKeyNode = {KeyIx, LockedHead};
  58. if (isNotLocked(LockedHead, KeyIx)){
  59. if (lockOne(env, &ThePid, KeyIx, Val)){
  60. LockedHead = &OneKeyNode;
  61. }else{
  62. uint64_t RExpected;
  63. KeyNode *temp = LockedHead;
  64. while (temp != NULL){
  65. RExpected = Val;
  66. LockSlot[temp->KeyIx].compare_exchange_strong(RExpected, 0);
  67. temp = temp->next;
  68. }
  69. }
  70. }
  71. }
  72. return atomTrue;
  73. }else{
  74. int KeyIx;
  75. KeyIx = enif_hash(ERL_NIF_INTERNAL_HASH, argv[0], HashSalt) % LockSize;
  76. ErlNifPid ThePid;
  77. enif_self(env, &ThePid);
  78. uint64_t Val = (uint64_t)(ThePid.pid);
  79. if (lockOne(env, &ThePid, KeyIx, Val)){
  80. return atomTrue;
  81. }else{
  82. return atomFalse;
  83. }
  84. }
  85. }
  86. ERL_NIF_TERM releaseLock(ErlNifEnv *env, int, const ERL_NIF_TERM argv[]){
  87. if (enif_is_list(env, argv[0])){
  88. ERL_NIF_TERM allList = argv[0];
  89. ERL_NIF_TERM head;
  90. ErlNifPid ThePid;
  91. enif_self(env, &ThePid);
  92. uint64_t Expected = (uint64_t)(ThePid.pid);
  93. uint64_t RExpected;
  94. int KeyIx;
  95. int isAllOk = 1;
  96. while (enif_get_list_cell(env, allList, &head, &allList)){
  97. KeyIx = enif_hash(ERL_NIF_INTERNAL_HASH, head, HashSalt) % LockSize;
  98. RExpected = Expected;
  99. if (!LockSlot[KeyIx].compare_exchange_strong(RExpected, 0)){
  100. isAllOk = 0;
  101. }
  102. }
  103. return isAllOk > 0 ? atomTrue : atomFalse;
  104. }else{
  105. int KeyIx;
  106. KeyIx = enif_hash(ERL_NIF_INTERNAL_HASH, argv[0], HashSalt) % LockSize;
  107. ErlNifPid ThePid;
  108. enif_self(env, &ThePid);
  109. uint64_t Expected = (uint64_t)(ThePid.pid);
  110. if (LockSlot[KeyIx].compare_exchange_strong(Expected, 0)){
  111. return atomTrue;
  112. }else{
  113. return atomFalse;
  114. }
  115. }
  116. }
  117. ERL_NIF_TERM getLockPid(ErlNifEnv *env, int, const ERL_NIF_TERM argv[]){
  118. int KeyIx;
  119. KeyIx = enif_hash(ERL_NIF_INTERNAL_HASH, argv[0], HashSalt) % LockSize;
  120. ErlNifPid ThePid;
  121. uint64_t Var = LockSlot[KeyIx].load();
  122. if (Var > 0){
  123. ThePid.pid = (ERL_NIF_TERM)Var;
  124. return enif_make_pid(env, &ThePid);
  125. }else{
  126. return atomUndefined;
  127. }
  128. }
  129. static ErlNifFunc nifFuncs[] = {
  130. {"tryLock", 1, tryLock},
  131. {"releaseLock", 1, releaseLock},
  132. {"getLockPid", 1, getLockPid}
  133. };
  134. ERL_NIF_INIT(eNifLock, nifFuncs, &nifLoad, NULL, NULL, NULL)