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.

239 lines
4.9 KiB

2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
8 years ago
  1. /*
  2. FUSE: Filesystem in Userspace
  3. Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
  4. This program can be distributed under the terms of the GNU LGPLv2.
  5. See the file COPYING.LIB.
  6. */
  7. #include "pool.hpp"
  8. #include "fuse_i.h"
  9. #include "fuse_kernel.h"
  10. #include "fuse_lowlevel.h"
  11. #include "fuse_misc.h"
  12. #include <errno.h>
  13. #include <semaphore.h>
  14. #include <signal.h>
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17. #include <string.h>
  18. #include <sys/time.h>
  19. #include <unistd.h>
  20. #include <unistd.h>
  21. /* Environment var controlling the thread stack size */
  22. #define ENVNAME_THREAD_STACK "FUSE_THREAD_STACK"
  23. struct fuse_worker
  24. {
  25. struct fuse_worker *prev;
  26. struct fuse_worker *next;
  27. pthread_t thread_id;
  28. size_t bufsize;
  29. char *buf;
  30. struct fuse_mt *mt;
  31. };
  32. struct fuse_mt
  33. {
  34. struct fuse_session *se;
  35. struct fuse_worker main;
  36. sem_t finish;
  37. int exit;
  38. int error;
  39. };
  40. static
  41. void
  42. list_add_worker(struct fuse_worker *w,
  43. struct fuse_worker *next)
  44. {
  45. struct fuse_worker *prev = next->prev;
  46. w->next = next;
  47. w->prev = prev;
  48. prev->next = w;
  49. next->prev = w;
  50. }
  51. static void list_del_worker(struct fuse_worker *w)
  52. {
  53. struct fuse_worker *prev = w->prev;
  54. struct fuse_worker *next = w->next;
  55. prev->next = next;
  56. next->prev = prev;
  57. }
  58. static int fuse_loop_start_thread(struct fuse_mt *mt);
  59. thread_pool tp;
  60. static
  61. void*
  62. fuse_do_work(void *data)
  63. {
  64. struct fuse_worker *w = (struct fuse_worker *) data;
  65. struct fuse_mt *mt = w->mt;
  66. while(!fuse_session_exited(mt->se))
  67. {
  68. int res;
  69. struct fuse_buf fbuf = {0};
  70. fbuf.mem = (char*)malloc(w->bufsize);
  71. fbuf.size = w->bufsize;
  72. pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
  73. res = mt->se->receive_buf(mt->se,&fbuf,mt->se->ch);
  74. pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
  75. if(res == -EINTR)
  76. continue;
  77. if(res <= 0)
  78. {
  79. if(res < 0)
  80. {
  81. mt->se->exited = 1;
  82. mt->error = -1;
  83. }
  84. break;
  85. }
  86. if(mt->exit)
  87. return NULL;
  88. tp.enqueue_work([=] {
  89. mt->se->process_buf(mt->se->data,fbuf.mem,mt->se->ch);
  90. free(fbuf.mem);
  91. });
  92. }
  93. sem_post(&mt->finish);
  94. return NULL;
  95. }
  96. int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg)
  97. {
  98. sigset_t oldset;
  99. sigset_t newset;
  100. int res;
  101. pthread_attr_t attr;
  102. char *stack_size;
  103. /* Override default stack size */
  104. pthread_attr_init(&attr);
  105. stack_size = getenv(ENVNAME_THREAD_STACK);
  106. if(stack_size && pthread_attr_setstacksize(&attr, atoi(stack_size)))
  107. fprintf(stderr, "fuse: invalid stack size: %s\n", stack_size);
  108. /* Disallow signal reception in worker threads */
  109. sigfillset(&newset);
  110. pthread_sigmask(SIG_BLOCK,&newset,&oldset);
  111. res = pthread_create(thread_id, &attr, func, arg);
  112. pthread_sigmask(SIG_SETMASK,&oldset,NULL);
  113. pthread_attr_destroy(&attr);
  114. if(res != 0) {
  115. fprintf(stderr, "fuse: error creating thread: %s\n",
  116. strerror(res));
  117. return -1;
  118. }
  119. return 0;
  120. }
  121. static int fuse_loop_start_thread(struct fuse_mt *mt)
  122. {
  123. int res;
  124. struct fuse_worker *w = (struct fuse_worker*)malloc(sizeof(struct fuse_worker));
  125. if(!w) {
  126. fprintf(stderr, "fuse: failed to allocate worker structure\n");
  127. return -1;
  128. }
  129. memset(w, 0, sizeof(struct fuse_worker));
  130. w->bufsize = fuse_chan_bufsize(mt->se->ch);
  131. w->buf = (char*)calloc(w->bufsize,1);
  132. w->mt = mt;
  133. if(!w->buf) {
  134. fprintf(stderr, "fuse: failed to allocate read buffer\n");
  135. free(w);
  136. return -1;
  137. }
  138. res = fuse_start_thread(&w->thread_id, fuse_do_work, w);
  139. if(res == -1) {
  140. free(w->buf);
  141. free(w);
  142. return -1;
  143. }
  144. list_add_worker(w, &mt->main);
  145. return 0;
  146. }
  147. static void fuse_join_worker(struct fuse_worker *w)
  148. {
  149. pthread_join(w->thread_id, NULL);
  150. list_del_worker(w);
  151. free(w->buf);
  152. free(w);
  153. }
  154. static int number_of_threads(void)
  155. {
  156. #ifdef _SC_NPROCESSORS_ONLN
  157. return sysconf(_SC_NPROCESSORS_ONLN);
  158. #endif
  159. return 4;
  160. }
  161. int
  162. fuse_session_loop_mt(struct fuse_session *se_,
  163. const int threads_)
  164. {
  165. int i;
  166. int err;
  167. int threads;
  168. struct fuse_mt mt;
  169. struct fuse_worker *w;
  170. memset(&mt,0,sizeof(struct fuse_mt));
  171. mt.se = se_;
  172. mt.error = 0;
  173. mt.main.thread_id = pthread_self();
  174. mt.main.prev = mt.main.next = &mt.main;
  175. sem_init(&mt.finish,0,0);
  176. threads = ((threads_ > 0) ? threads_ : number_of_threads());
  177. if(threads_ < 0)
  178. threads /= -threads_;
  179. if(threads == 0)
  180. threads = 1;
  181. err = 0;
  182. for(i = 0; (i < threads) && !err; i++)
  183. err = fuse_loop_start_thread(&mt);
  184. if(!err)
  185. {
  186. /* sem_wait() is interruptible */
  187. while(!fuse_session_exited(se_))
  188. sem_wait(&mt.finish);
  189. for(w = mt.main.next; w != &mt.main; w = w->next)
  190. pthread_cancel(w->thread_id);
  191. mt.exit = 1;
  192. while(mt.main.next != &mt.main)
  193. fuse_join_worker(mt.main.next);
  194. err = mt.error;
  195. }
  196. sem_destroy(&mt.finish);
  197. fuse_session_reset(se_);
  198. return err;
  199. }