mutex_test.d 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. // mutex example
  2. // http://127.0.0.1:8080/ + 'ws://' + window.location.host + '/ws_mutex';
  3. import std.stdio : writeln;
  4. import std.string;
  5. import std.array;
  6. import std.algorithm;
  7. import vibe.core.core;
  8. import vibe.http.router;
  9. import vibe.http.server;
  10. import vibe.http.fileserver;
  11. import vibe.http.websockets;
  12. import vibe.core.log;
  13. import core.sync.mutex : Mutex;
  14. import std.datetime : SysTime, Clock;
  15. import std.concurrency : spawn;
  16. import vibe.core.concurrency;
  17. import vibe.core.core : Task, sleep;
  18. import core.time : Duration, dur;
  19. struct UserState{
  20. string client_id;
  21. SysTime last_online_at;
  22. this(string client_id, SysTime last_online_at) pure nothrow @safe{
  23. this.client_id = client_id;
  24. this.last_online_at = last_online_at;
  25. }
  26. }
  27. alias UserStateMap = UserState[string];
  28. class UserStateManager{
  29. private UserStateMap states;
  30. private Object lockObj;
  31. this(){
  32. lockObj = new Object();
  33. }
  34. bool contains(string client_id){
  35. synchronized(lockObj){
  36. return (client_id in states) !is null;
  37. }
  38. }
  39. void addOrUpdate(string client_id){
  40. synchronized(lockObj){
  41. auto now = Clock.currTime();
  42. states.remove(client_id);
  43. states[client_id] = UserState(client_id, now);
  44. }
  45. }
  46. void cleanup(){
  47. synchronized(lockObj){
  48. auto now = Clock.currTime();
  49. auto toRemove = states.byKey
  50. .filter!(k => (now - states[k].last_online_at).total!"seconds" > 30)
  51. .array;
  52. foreach(client_id; toRemove){
  53. states.remove(client_id);
  54. }
  55. }
  56. }
  57. size_t length() const{
  58. synchronized(lockObj){
  59. return states.length;
  60. }
  61. }
  62. }
  63. __gshared UserStateManager userStateManager;
  64. void startCleanupTask(){
  65. while(true){
  66. writeln("startCleanupTask();");
  67. userStateManager.cleanup();
  68. //writeln("88 userStateManager.states is null? ", userStateManager.states is null);
  69. //sleep(dur!"hours"(1)); // every 1 hour = 3600 sec
  70. sleep(dur!"seconds"(30)); // every 30 sec
  71. }
  72. }
  73. void init_mutex(){
  74. userStateManager = new UserStateManager();
  75. //userStateManager.addOrUpdate("123");
  76. //writeln("99 userStateManager.states is null? ", userStateManager.states is null);
  77. //sleep(dur!"seconds"(1));
  78. spawn(&startCleanupTask); // clean inactive user state
  79. }
  80. void ws_mutex_handle(scope WebSocket sock){
  81. string client_id = "";
  82. if("client_id" in sock.request.query){
  83. client_id = sock.request.query["client_id"];
  84. //writeln("found client_id = ", client_id);
  85. }else{
  86. //writeln("client_id not found");
  87. throw new StringException("client_id not found");
  88. }
  89. bool is_new_client = true;
  90. if(userStateManager.contains(client_id)){
  91. is_new_client = false;
  92. writeln("Reconnection from existing client: ", client_id);
  93. }else{
  94. writeln("New client connected: ", client_id);
  95. }
  96. userStateManager.addOrUpdate(client_id);
  97. while(sock.waitForData()){
  98. auto msg = sock.receiveText();
  99. sock.send(msg ~ " :)");
  100. }
  101. writeln("after disconnect");
  102. }
  103. /*
  104. ./vtest
  105. Listening for requests on http://127.0.0.1:8080/
  106. //99 userStateManager.states is null? true
  107. Please open http://127.0.0.1:8080/ in your browser.
  108. startCleanupTask();
  109. //88 userStateManager.states is null? true
  110. startCleanupTask();
  111. //88 userStateManager.states is null? true
  112. New client connected: zkytkcKzeGaDfvfDs6y5
  113. after disconnect
  114. Reconnection from existing client: zkytkcKzeGaDfvfDs6y5
  115. after disconnect
  116. startCleanupTask();
  117. //88 userStateManager.states is null? false
  118. New client connected: zkytkcKzeGaDfvfDs6y5
  119. after disconnect
  120. startCleanupTask();
  121. //88 userStateManager.states is null? false
  122. */