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.

223 lines
5.9 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. /* -*- coding: utf-8 -*-
  2. * ----------------------------------------------------------------------
  3. * Copyright © 2013-2015, RedJack, LLC.
  4. * All rights reserved.
  5. *
  6. * Please see the COPYING file in this distribution for license details.
  7. * ----------------------------------------------------------------------
  8. */
  9. #if defined(__linux)
  10. /* This is needed on Linux to get the pthread_setname_np function. */
  11. #if !defined(_GNU_SOURCE)
  12. #define _GNU_SOURCE 1
  13. #endif
  14. #endif
  15. #include <assert.h>
  16. #include <string.h>
  17. #include <pthread.h>
  18. #include "libcork/core/allocator.h"
  19. #include "libcork/core/error.h"
  20. #include "libcork/core/types.h"
  21. #include "libcork/ds/buffer.h"
  22. #include "libcork/threads/basics.h"
  23. /*-----------------------------------------------------------------------
  24. * Current thread
  25. */
  26. static volatile cork_thread_id last_thread_descriptor = 0;
  27. struct cork_thread {
  28. const char *name;
  29. cork_thread_id id;
  30. pthread_t thread_id;
  31. void *user_data;
  32. cork_free_f free_user_data;
  33. cork_run_f run;
  34. cork_error error_code;
  35. struct cork_buffer error_message;
  36. bool started;
  37. bool joined;
  38. };
  39. struct cork_thread_descriptor {
  40. struct cork_thread *current_thread;
  41. cork_thread_id id;
  42. };
  43. cork_tls(struct cork_thread_descriptor, cork_thread_descriptor);
  44. struct cork_thread *
  45. cork_current_thread_get(void)
  46. {
  47. struct cork_thread_descriptor *desc = cork_thread_descriptor_get();
  48. return desc->current_thread;
  49. }
  50. cork_thread_id
  51. cork_current_thread_get_id(void)
  52. {
  53. struct cork_thread_descriptor *desc = cork_thread_descriptor_get();
  54. if (CORK_UNLIKELY(desc->id == 0)) {
  55. if (desc->current_thread == NULL) {
  56. desc->id = cork_uint_atomic_add(&last_thread_descriptor, 1);
  57. } else {
  58. desc->id = desc->current_thread->id;
  59. }
  60. }
  61. return desc->id;
  62. }
  63. /*-----------------------------------------------------------------------
  64. * Threads
  65. */
  66. struct cork_thread *
  67. cork_thread_new(const char *name,
  68. void *user_data, cork_free_f free_user_data,
  69. cork_run_f run)
  70. {
  71. struct cork_thread *self = cork_new(struct cork_thread);
  72. self->name = cork_strdup(name);
  73. self->id = cork_uint_atomic_add(&last_thread_descriptor, 1);
  74. self->user_data = user_data;
  75. self->free_user_data = free_user_data;
  76. self->run = run;
  77. self->error_code = CORK_ERROR_NONE;
  78. cork_buffer_init(&self->error_message);
  79. self->started = false;
  80. self->joined = false;
  81. return self;
  82. }
  83. static void
  84. cork_thread_free_private(struct cork_thread *self)
  85. {
  86. cork_strfree(self->name);
  87. cork_free_user_data(self);
  88. cork_buffer_done(&self->error_message);
  89. cork_delete(struct cork_thread, self);
  90. }
  91. void
  92. cork_thread_free(struct cork_thread *self)
  93. {
  94. assert(!self->started);
  95. cork_thread_free_private(self);
  96. }
  97. const char *
  98. cork_thread_get_name(struct cork_thread *self)
  99. {
  100. return self->name;
  101. }
  102. cork_thread_id
  103. cork_thread_get_id(struct cork_thread *self)
  104. {
  105. return self->id;
  106. }
  107. #define PTHREADS_MAX_THREAD_NAME_LENGTH 16
  108. static void *
  109. cork_thread_pthread_run(void *vself)
  110. {
  111. int rc;
  112. struct cork_thread *self = vself;
  113. struct cork_thread_descriptor *desc = cork_thread_descriptor_get();
  114. #if defined(__APPLE__) && defined(__MACH__)
  115. char thread_name[PTHREADS_MAX_THREAD_NAME_LENGTH];
  116. #endif
  117. desc->current_thread = self;
  118. desc->id = self->id;
  119. rc = self->run(self->user_data);
  120. #if defined(__APPLE__) && defined(__MACH__)
  121. /* On Mac OS X, we set the name of the current thread, not of an arbitrary
  122. * thread of our choosing. */
  123. strncpy(thread_name, self->name, PTHREADS_MAX_THREAD_NAME_LENGTH);
  124. thread_name[PTHREADS_MAX_THREAD_NAME_LENGTH - 1] = '\0';
  125. pthread_setname_np(thread_name);
  126. #endif
  127. /* If an error occurred in the body of the thread, save the error into the
  128. * cork_thread object so that we can propagate that error when some calls
  129. * cork_thread_join. */
  130. if (CORK_UNLIKELY(rc != 0)) {
  131. if (CORK_LIKELY(cork_error_occurred())) {
  132. self->error_code = cork_error_code();
  133. cork_buffer_set_string(&self->error_message, cork_error_message());
  134. } else {
  135. self->error_code = CORK_UNKNOWN_ERROR;
  136. cork_buffer_set_string(&self->error_message, "Unknown error");
  137. }
  138. }
  139. return NULL;
  140. }
  141. int
  142. cork_thread_start(struct cork_thread *self)
  143. {
  144. int rc;
  145. pthread_t thread_id;
  146. #if defined(__linux) && ((__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 12))
  147. char thread_name[PTHREADS_MAX_THREAD_NAME_LENGTH];
  148. #endif
  149. assert(!self->started);
  150. rc = pthread_create(&thread_id, NULL, cork_thread_pthread_run, self);
  151. if (CORK_UNLIKELY(rc != 0)) {
  152. cork_system_error_set_explicit(rc);
  153. return -1;
  154. }
  155. #if defined(__linux) && ((__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 12))
  156. /* On Linux we choose which thread to name via an explicit thread ID.
  157. * However, pthread_setname_np() isn't supported on versions of glibc
  158. * earlier than 2.12. So we need to check for a MINOR version of 12 or
  159. * higher. */
  160. strncpy(thread_name, self->name, PTHREADS_MAX_THREAD_NAME_LENGTH);
  161. thread_name[PTHREADS_MAX_THREAD_NAME_LENGTH - 1] = '\0';
  162. pthread_setname_np(thread_id, thread_name);
  163. #endif
  164. self->thread_id = thread_id;
  165. self->started = true;
  166. return 0;
  167. }
  168. int
  169. cork_thread_join(struct cork_thread *self)
  170. {
  171. int rc;
  172. assert(self->started && !self->joined);
  173. rc = pthread_join(self->thread_id, NULL);
  174. if (CORK_UNLIKELY(rc != 0)) {
  175. cork_system_error_set_explicit(rc);
  176. cork_thread_free_private(self);
  177. return -1;
  178. }
  179. if (CORK_UNLIKELY(self->error_code != CORK_ERROR_NONE)) {
  180. cork_error_set_printf
  181. (self->error_code, "Error from thread %s: %s",
  182. self->name, (char *) self->error_message.buf);
  183. cork_thread_free_private(self);
  184. return -1;
  185. }
  186. cork_thread_free_private(self);
  187. return 0;
  188. }