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.

136 lines
3.7 KiB

1 year ago
1 year ago
1 year ago
  1. #include "erl_nif.h"
  2. #include <atomic>
  3. #include <set>
  4. using namespace std;
  5. const int LockSize = 2097152;
  6. const int HashSalt = 786234121;
  7. atomic<uint64_t> LockSlot[LockSize];
  8. ERL_NIF_TERM atomTrue;
  9. ERL_NIF_TERM atomFalse;
  10. ERL_NIF_TERM atomUndefined;
  11. int nifLoad(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM){
  12. atomTrue = enif_make_atom(env, "true");
  13. atomFalse = enif_make_atom(env, "false");
  14. atomUndefined = enif_make_atom(env, "undefined");
  15. return 0;
  16. }
  17. bool lockOne(ErlNifEnv *env, ErlNifPid *ThePid, int KeyIx, uint64_t Val){
  18. uint64_t Expected = 0;
  19. if (LockSlot[KeyIx].compare_exchange_strong(Expected, Val)){
  20. return true;
  21. }else{
  22. ThePid->pid = (ERL_NIF_TERM)Expected;
  23. if (enif_is_process_alive(env, ThePid)){
  24. return false;
  25. }else{
  26. if (LockSlot[KeyIx].compare_exchange_strong(Expected, Val)){
  27. return true;
  28. }else{
  29. return false;
  30. }
  31. }
  32. }
  33. }
  34. ERL_NIF_TERM tryLock(ErlNifEnv *env, int, const ERL_NIF_TERM argv[]){
  35. int KeyIx;
  36. enif_get_int(env, argv[0], &KeyIx);
  37. ErlNifPid ThePid;
  38. enif_self(env, &ThePid);
  39. uint64_t Val = (uint64_t)(ThePid.pid);
  40. if (lockOne(env, &ThePid, KeyIx, Val)){
  41. return atomTrue;
  42. }else{
  43. return atomFalse;
  44. }
  45. }
  46. ERL_NIF_TERM tryLocks(ErlNifEnv *env, int, const ERL_NIF_TERM argv[]){
  47. ERL_NIF_TERM allList = argv[0];
  48. ERL_NIF_TERM head;
  49. ErlNifPid ThePid;
  50. enif_self(env, &ThePid);
  51. uint64_t Val = (uint64_t)(ThePid.pid);
  52. int KeyIx;
  53. int cnt = -1;
  54. while (enif_get_list_cell(env, allList, &head, &allList)){
  55. enif_get_int(env, head, &KeyIx);
  56. if(lockOne(env, &ThePid, KeyIx, Val)){
  57. cnt++;
  58. }else{
  59. uint64_t RExpected;
  60. allList = argv[0];
  61. for(int i = 0; i <= cnt; i++){
  62. enif_get_list_cell(env, allList, &head, &allList);
  63. enif_get_int(env, head, &KeyIx);
  64. RExpected = Val;
  65. LockSlot[KeyIx].compare_exchange_strong(RExpected, 0);
  66. }
  67. return atomFalse;
  68. }
  69. }
  70. return atomTrue;
  71. }
  72. ERL_NIF_TERM releaseLock(ErlNifEnv *env, int, const ERL_NIF_TERM argv[]){
  73. int KeyIx;
  74. enif_get_int(env, argv[0], &KeyIx);
  75. ErlNifPid ThePid;
  76. enif_self(env, &ThePid);
  77. uint64_t Expected = (uint64_t)(ThePid.pid);
  78. if (LockSlot[KeyIx].compare_exchange_strong(Expected, 0)){
  79. return atomTrue;
  80. }else{
  81. return atomFalse;
  82. }
  83. }
  84. ERL_NIF_TERM releaseLocks(ErlNifEnv *env, int, const ERL_NIF_TERM argv[]){
  85. ERL_NIF_TERM allList = argv[0];
  86. ERL_NIF_TERM head;
  87. ErlNifPid ThePid;
  88. enif_self(env, &ThePid);
  89. uint64_t Expected = (uint64_t)(ThePid.pid);
  90. uint64_t RExpected;
  91. int KeyIx;
  92. int isAllOk = 1;
  93. while (enif_get_list_cell(env, allList, &head, &allList)){
  94. enif_get_int(env, head, &KeyIx);
  95. RExpected = Expected;
  96. if (!LockSlot[KeyIx].compare_exchange_strong(RExpected, 0)){
  97. isAllOk = 0;
  98. }
  99. }
  100. return isAllOk > 0 ? atomTrue : atomFalse;
  101. }
  102. ERL_NIF_TERM getLockPid(ErlNifEnv *env, int, const ERL_NIF_TERM argv[]){
  103. int KeyIx;
  104. KeyIx = enif_hash(ERL_NIF_INTERNAL_HASH, argv[0], HashSalt) % LockSize;
  105. ErlNifPid ThePid;
  106. uint64_t Var = LockSlot[KeyIx].load();
  107. if (Var > 0){
  108. ThePid.pid = (ERL_NIF_TERM)Var;
  109. return enif_make_pid(env, &ThePid);
  110. }else{
  111. return atomUndefined;
  112. }
  113. }
  114. static ErlNifFunc nifFuncs[] = {
  115. {"tryLock", 1, tryLock},
  116. {"tryLocks", 1, tryLocks},
  117. {"releaseLock", 1, releaseLock},
  118. {"releaseLocks", 1, releaseLocks},
  119. {"getLockPid", 1, getLockPid}
  120. };
  121. ERL_NIF_INIT(eNifLock, nifFuncs, &nifLoad, NULL, NULL, NULL)