25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

195 lines
5.1 KiB

  1. // Copyright (c) 2014-2015, Loïc Hoguin <essen@ninenines.eu>
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  8. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  9. // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  10. // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  11. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  12. // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  13. // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  14. #include "nif_helpers.h"
  15. #include <sys/queue.h>
  16. #include <stdarg.h>
  17. extern ERL_NIF_TERM atom_ok;
  18. extern ERL_NIF_TERM atom__nif_thread_ret_;
  19. typedef struct nif_thread_message {
  20. TAILQ_ENTRY(nif_thread_message) next_entry;
  21. ErlNifPid* from_pid;
  22. void* function;
  23. nif_thread_arg* args;
  24. } nif_thread_message;
  25. typedef TAILQ_HEAD(nif_thread_mailbox, nif_thread_message) nif_thread_mailbox;
  26. typedef struct {
  27. ErlNifTid tid;
  28. ErlNifMutex* lock;
  29. ErlNifCond* cond;
  30. nif_thread_mailbox* mailbox;
  31. } nif_thread_state;
  32. // Message.
  33. static nif_thread_message* nif_thread_message_alloc(void* f, nif_thread_arg* args, ErlNifPid* pid)
  34. {
  35. nif_thread_message* msg = (nif_thread_message*)enif_alloc(sizeof(nif_thread_message));
  36. msg->from_pid = pid;
  37. msg->function = f;
  38. msg->args = args;
  39. return msg;
  40. }
  41. static void nif_thread_message_free(nif_thread_message* msg)
  42. {
  43. enif_free(msg->from_pid);
  44. enif_free(msg->args);
  45. enif_free(msg);
  46. }
  47. // Calls and casts.
  48. static ERL_NIF_TERM nif_thread_send(nif_thread_state* st, nif_thread_message* msg)
  49. {
  50. enif_mutex_lock(st->lock);
  51. TAILQ_INSERT_TAIL(st->mailbox, msg, next_entry);
  52. enif_cond_signal(st->cond);
  53. enif_mutex_unlock(st->lock);
  54. return atom_ok;
  55. }
  56. ERL_NIF_TERM nif_thread_cast(ErlNifEnv* env, void (*f)(nif_thread_arg*), int a, ...)
  57. {
  58. va_list ap;
  59. int i;
  60. nif_thread_arg* args = (nif_thread_arg*)enif_alloc(a * sizeof(nif_thread_arg));
  61. va_start(ap, a);
  62. for (i = 0; i < a; i++)
  63. args[i] = va_arg(ap, void*);
  64. va_end(ap);
  65. nif_thread_message* msg = nif_thread_message_alloc(f, args, NULL);
  66. return nif_thread_send((nif_thread_state*)enif_priv_data(env), msg);
  67. }
  68. ERL_NIF_TERM nif_thread_call(ErlNifEnv* env, ERL_NIF_TERM (*f)(ErlNifEnv*, nif_thread_arg*), int a, ...)
  69. {
  70. va_list ap;
  71. int i;
  72. nif_thread_arg* args = (nif_thread_arg*)enif_alloc(a * sizeof(nif_thread_arg));
  73. va_start(ap, a);
  74. for (i = 0; i < a; i++)
  75. args[i] = va_arg(ap, void*);
  76. va_end(ap);
  77. ErlNifPid* pid = (ErlNifPid*)enif_alloc(sizeof(ErlNifPid));
  78. nif_thread_message* msg = nif_thread_message_alloc((void*)f, args, enif_self(env, pid));
  79. return nif_thread_send((nif_thread_state*)enif_priv_data(env), msg);
  80. }
  81. // Main thread loop.
  82. static int nif_thread_receive(nif_thread_state* st, nif_thread_message** msg)
  83. {
  84. enif_mutex_lock(st->lock);
  85. while (TAILQ_EMPTY(st->mailbox))
  86. enif_cond_wait(st->cond, st->lock);
  87. *msg = TAILQ_FIRST(st->mailbox);
  88. TAILQ_REMOVE(st->mailbox, TAILQ_FIRST(st->mailbox), next_entry);
  89. enif_mutex_unlock(st->lock);
  90. if ((*msg)->function == NULL)
  91. return 0;
  92. return 1;
  93. }
  94. static void nif_thread_handle(ErlNifEnv* env, nif_thread_state* st, nif_thread_message* msg)
  95. {
  96. if (msg->from_pid == NULL) {
  97. void (*cast)(nif_thread_arg*) = msg->function;
  98. cast(msg->args);
  99. } else {
  100. ERL_NIF_TERM (*call)(ErlNifEnv*, nif_thread_arg*) = msg->function;
  101. ERL_NIF_TERM ret = call(env, msg->args);
  102. enif_send(NULL, msg->from_pid, env,
  103. enif_make_tuple2(env, atom__nif_thread_ret_, ret));
  104. enif_clear_env(env);
  105. }
  106. nif_thread_message_free(msg);
  107. }
  108. static void* nif_main_thread(void* obj)
  109. {
  110. ErlNifEnv* env = enif_alloc_env();
  111. nif_thread_state* st = (nif_thread_state*)obj;
  112. nif_thread_message* msg;
  113. while (nif_thread_receive(st, &msg))
  114. nif_thread_handle(env, st, msg);
  115. enif_free_env(env);
  116. return NULL;
  117. }
  118. // Main thread creation/destruction.
  119. void* nif_create_main_thread(char* name)
  120. {
  121. nif_thread_state* st = (nif_thread_state*)enif_alloc(sizeof(nif_thread_state));
  122. st->lock = enif_mutex_create("nif_thread_mailbox_lock");
  123. st->cond = enif_cond_create("nif_thread_mailbox_cond");
  124. st->mailbox = (nif_thread_mailbox*)enif_alloc(sizeof(nif_thread_mailbox));
  125. TAILQ_INIT(st->mailbox);
  126. #if defined(__APPLE__) && defined(__MACH__)
  127. // On OSX we identify ourselves as the main thread to ensure that
  128. // we are compatible with libraries that require it. For example
  129. // this is necessary with SDL2 in order to receive input events.
  130. erl_drv_steal_main_thread(name, &(st->tid), nif_main_thread, st, NULL);
  131. #else
  132. enif_thread_create(name, &(st->tid), nif_main_thread, st, NULL);
  133. #endif
  134. return (void*)st;
  135. }
  136. void nif_destroy_main_thread(void* void_st)
  137. {
  138. nif_thread_state* st = (nif_thread_state*)void_st;
  139. nif_thread_message* msg = nif_thread_message_alloc(NULL, NULL, NULL);
  140. nif_thread_send(st, msg);
  141. enif_thread_join(st->tid, NULL);
  142. enif_cond_destroy(st->cond);
  143. enif_mutex_destroy(st->lock);
  144. enif_free(st->mailbox);
  145. enif_free(st);
  146. }