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.

248 lines
5.0 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
7 years ago
5 years ago
5 years ago
5 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 "fuse_chan.h"
  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 <stdint.h>
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <string.h>
  19. #include <sys/time.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. struct fuse_mt *mt;
  28. pthread_t thread_id;
  29. fuse_chan_t *ch;
  30. };
  31. struct fuse_mt
  32. {
  33. struct fuse_session *se;
  34. struct fuse_worker main;
  35. sem_t finish;
  36. int exit;
  37. int error;
  38. };
  39. static
  40. void
  41. list_add_worker(struct fuse_worker *w,
  42. struct fuse_worker *next)
  43. {
  44. struct fuse_worker *prev = next->prev;
  45. w->next = next;
  46. w->prev = prev;
  47. prev->next = w;
  48. next->prev = w;
  49. }
  50. static
  51. void
  52. list_del_worker(struct fuse_worker *w)
  53. {
  54. struct fuse_worker *prev = w->prev;
  55. struct fuse_worker *next = w->next;
  56. prev->next = next;
  57. next->prev = prev;
  58. }
  59. static int fuse_loop_start_thread(struct fuse_mt *mt);
  60. static
  61. void*
  62. fuse_do_work(void *data)
  63. {
  64. struct fuse_mt *mt;
  65. fuse_chan_t *ch;
  66. struct fuse_worker *w;
  67. w = (struct fuse_worker*)data;
  68. mt = w->mt;
  69. ch = w->ch;
  70. while(!fuse_session_exited(mt->se))
  71. {
  72. int res;
  73. struct fuse_buf fbuf = { .mem = ch->buf, .size = ch->bufsize };
  74. pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
  75. res = fuse_ll_receive_buf(mt->se,&fbuf,ch);
  76. pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
  77. if(res == -EINTR)
  78. continue;
  79. if(res == 0)
  80. break;
  81. if(res < 0)
  82. {
  83. fuse_session_exit(mt->se);
  84. mt->error = -1;
  85. break;
  86. }
  87. if(mt->exit)
  88. return NULL;
  89. fuse_ll_process_buf(mt->se->data,&fbuf,ch);
  90. }
  91. sem_post(&mt->finish);
  92. return NULL;
  93. }
  94. int
  95. fuse_start_thread(pthread_t *thread_id,
  96. void *(*func)(void *),
  97. void *arg)
  98. {
  99. int res;
  100. sigset_t oldset;
  101. sigset_t newset;
  102. pthread_attr_t attr;
  103. char *stack_size;
  104. /* Override default stack size */
  105. pthread_attr_init(&attr);
  106. stack_size = getenv(ENVNAME_THREAD_STACK);
  107. if(stack_size && pthread_attr_setstacksize(&attr,atoi(stack_size)))
  108. fprintf(stderr, "fuse: invalid stack size: %s\n", stack_size);
  109. /* Disallow signal reception in worker threads */
  110. sigemptyset(&newset);
  111. sigaddset(&newset, SIGTERM);
  112. sigaddset(&newset, SIGINT);
  113. sigaddset(&newset, SIGHUP);
  114. sigaddset(&newset, SIGQUIT);
  115. pthread_sigmask(SIG_BLOCK, &newset, &oldset);
  116. res = pthread_create(thread_id, &attr, func, arg);
  117. pthread_sigmask(SIG_SETMASK, &oldset, NULL);
  118. pthread_attr_destroy(&attr);
  119. if(res != 0)
  120. {
  121. fprintf(stderr, "fuse: error creating thread: %s\n",
  122. strerror(res));
  123. return -1;
  124. }
  125. return 0;
  126. }
  127. static
  128. int
  129. fuse_loop_start_thread(struct fuse_mt *mt)
  130. {
  131. int res;
  132. struct fuse_worker *w;
  133. w = malloc(sizeof(struct fuse_worker));
  134. if(!w)
  135. {
  136. fprintf(stderr, "fuse: failed to allocate worker structure\n");
  137. return -1;
  138. }
  139. memset(w,0,sizeof(struct fuse_worker));
  140. w->mt = mt;
  141. w->ch = fuse_chan_new(mt->se->devfuse_fd,FUSE_CHAN_SPLICE_READ|FUSE_CHAN_SPLICE_WRITE);
  142. if(w->ch == NULL)
  143. {
  144. fprintf(stderr, "fuse: failed to allocate fuse channel\n");
  145. free(w);
  146. return -1;
  147. }
  148. res = fuse_start_thread(&w->thread_id, fuse_do_work, w);
  149. if(res == -1)
  150. {
  151. free(w);
  152. return -1;
  153. }
  154. list_add_worker(w,&mt->main);
  155. return 0;
  156. }
  157. static void fuse_join_worker(struct fuse_worker *w)
  158. {
  159. pthread_join(w->thread_id, NULL);
  160. list_del_worker(w);
  161. fuse_chan_destroy(w->ch);
  162. free(w);
  163. }
  164. static int number_of_threads(void)
  165. {
  166. #ifdef _SC_NPROCESSORS_ONLN
  167. return sysconf(_SC_NPROCESSORS_ONLN);
  168. #endif
  169. return 4;
  170. }
  171. int fuse_session_loop_mt(struct fuse_session *se,
  172. const int threads_)
  173. {
  174. int i;
  175. int err;
  176. int threads;
  177. struct fuse_mt mt;
  178. struct fuse_worker *w;
  179. memset(&mt, 0, sizeof(struct fuse_mt));
  180. mt.se = se;
  181. mt.error = 0;
  182. mt.main.thread_id = pthread_self();
  183. mt.main.prev = mt.main.next = &mt.main;
  184. sem_init(&mt.finish, 0, 0);
  185. threads = ((threads_ > 0) ? threads_ : number_of_threads());
  186. if(threads_ < 0)
  187. threads /= -threads_;
  188. if(threads == 0)
  189. threads = 1;
  190. err = 0;
  191. for(i = 0; (i < threads) && !err; i++)
  192. err = fuse_loop_start_thread(&mt);
  193. if(!err)
  194. {
  195. /* sem_wait() is interruptible */
  196. while(!fuse_session_exited(se))
  197. sem_wait(&mt.finish);
  198. for(w = mt.main.next; w != &mt.main; w = w->next)
  199. pthread_cancel(w->thread_id);
  200. mt.exit = 1;
  201. while(mt.main.next != &mt.main)
  202. fuse_join_worker(mt.main.next);
  203. err = mt.error;
  204. }
  205. sem_destroy(&mt.finish);
  206. fuse_session_reset(se);
  207. return err;
  208. }