worker.cc 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. #include <pthread.h>
  2. extern "C" {
  3. #include <event2/thread.h>
  4. #include <event2/bufferevent.h>
  5. #include <event2/buffer.h>
  6. #include <event2/event.h>
  7. #include <event2/util.h>
  8. }
  9. #include <NTL/ZZ_p.h>
  10. #include <vector>
  11. #include <stdio.h>
  12. #include <errno.h>
  13. #include <signal.h>
  14. #include "cudadl.h"
  15. #include "evutils.h"
  16. #include "subproblem.h"
  17. NTL_CLIENT
  18. #define DEBUG
  19. typedef enum {
  20. WRKCCSTATE_AWAITCMD,
  21. WRKCCSTATE_RDPROBLEM,
  22. WRKCCSTATE_RDDPNODES,
  23. WRKCCSTATE_END
  24. } WrkCCState;
  25. struct WrkControllerConnInfo {
  26. WrkCCState state;
  27. unsigned short num_dpnodes;
  28. WrkControllerConnInfo() : state(WRKCCSTATE_AWAITCMD), num_dpnodes(0) {}
  29. };
  30. typedef enum { WT_NOT_RUNNING, WT_RUNNING, WT_SHOULD_STOP} WTState;
  31. static struct WrkControllerState {
  32. Subproblem *current_problem;
  33. unsigned short num_expected_dpnodes;
  34. vector<struct bufferevent *> dpnodes;
  35. unsigned short num_connected_dpnodes;
  36. WTState worker_thread_state;
  37. pthread_t worker_thread;
  38. WrkControllerState(): current_problem(NULL),
  39. worker_thread_state(WT_NOT_RUNNING) {}
  40. } wrkctrlstate;
  41. // ----- Below this line are the functions running in the worker thread.
  42. #if WORDS > 1
  43. #define DEMUXWORD 8
  44. #else
  45. #define DEMUXWORD 7
  46. #endif
  47. // This function is called from inside cuda_dl for each DP it encounters.
  48. // It calls the function named "dpcallback" directly. It would be
  49. // cleaner if this were passed as a function pointer to cuda_dl, but
  50. // that makes nvcc 3.1 segfault. :-p
  51. // dp points to an array of WORDS+7 unsigned ints:
  52. // - 1 word of threadID/blockID
  53. // - WORDS words of the dp value
  54. // - 3 words of a
  55. // - 3 words of b
  56. bool dpcallback(void *cbdata, unsigned int *dpwords)
  57. {
  58. unsigned int demux = dpwords[DEMUXWORD];
  59. struct bufferevent *bev =
  60. wrkctrlstate.dpnodes[demux % wrkctrlstate.num_connected_dpnodes];
  61. bufferevent_write(bev, dpwords+1, (WORDS+6)*sizeof(unsigned int));
  62. // If worker_thread_state changes to WT_SHOULD_STOP, then signal to
  63. // stop computation by returning true. If for some reason, it
  64. // becomes WT_NOT_RUNNING (which it shouldn't), stop as well.
  65. return wrkctrlstate.worker_thread_state != WT_RUNNING;
  66. }
  67. static void *worker_thread_start(void *data)
  68. {
  69. ZZ_p::init(wrkctrlstate.current_problem->modulus);
  70. cuda_dl(to_ZZ_p(wrkctrlstate.current_problem->base),
  71. to_ZZ_p(wrkctrlstate.current_problem->target),
  72. wrkctrlstate.current_problem->order,
  73. wrkctrlstate.current_problem->modulus,
  74. wrkctrlstate.current_problem->dpfreq, NULL);
  75. return NULL;
  76. }
  77. // ----- Above this line are the functions running in the worker thread.
  78. // Below are the functions running in the main (communication) thread.
  79. static void stop_working(void)
  80. {
  81. #ifdef DEBUG
  82. cerr << "Stopping work\n";
  83. #endif
  84. if (wrkctrlstate.worker_thread_state != WT_NOT_RUNNING) {
  85. // Tell the worker thread to stop after its next kernel launch
  86. wrkctrlstate.worker_thread_state = WT_SHOULD_STOP;
  87. pthread_join(wrkctrlstate.worker_thread, NULL);
  88. wrkctrlstate.worker_thread_state = WT_NOT_RUNNING;
  89. }
  90. // Close the connections to the dpnodes
  91. vector<struct bufferevent *>::iterator bevit;
  92. for (bevit = wrkctrlstate.dpnodes.begin();
  93. bevit != wrkctrlstate.dpnodes.end(); ++bevit) {
  94. #ifdef DEBUG
  95. cerr << "Closing connection to " << *bevit << "\n";
  96. #endif
  97. bufferevent_free(*bevit);
  98. }
  99. wrkctrlstate.dpnodes.clear();
  100. wrkctrlstate.num_connected_dpnodes = 0;
  101. wrkctrlstate.num_expected_dpnodes = 0;
  102. // Careful! Subproblem uses NTL, so we must be sure we're not
  103. // multithreaded at this point.
  104. delete wrkctrlstate.current_problem;
  105. wrkctrlstate.current_problem = NULL;
  106. }
  107. static void start_working(void)
  108. {
  109. #ifdef DEBUG
  110. cerr << "Starting work\n";
  111. #endif
  112. wrkctrlstate.worker_thread_state = WT_RUNNING;
  113. if (pthread_create(&wrkctrlstate.worker_thread, NULL,
  114. worker_thread_start, NULL)) {
  115. wrkctrlstate.worker_thread = WT_NOT_RUNNING;
  116. cerr << "Could not start worker thread\n";
  117. }
  118. }
  119. static void dpconn_event_cb(struct bufferevent *bev, short events,
  120. void *ctx)
  121. {
  122. if (events & BEV_EVENT_CONNECTED) {
  123. // We have successfully connected to the dpnode
  124. #ifdef DEBUG
  125. cerr << "Connection established to dpnode " << bev << "\n";
  126. #endif
  127. bufferevent_enable(bev, EV_WRITE);
  128. ++wrkctrlstate.num_connected_dpnodes;
  129. if (wrkctrlstate.num_connected_dpnodes ==
  130. wrkctrlstate.num_expected_dpnodes) {
  131. start_working();
  132. }
  133. } else if (events & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
  134. #ifdef DEBUG
  135. cerr << "Closing connection to dpnode " << bev << " ";
  136. if (events & BEV_EVENT_EOF) {
  137. cerr << "EOF";
  138. }
  139. if (events & BEV_EVENT_ERROR) {
  140. cerr << "ERR (" << evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()) << ")";
  141. }
  142. cerr << "\n";
  143. #endif
  144. stop_working();
  145. }
  146. }
  147. static void controllerconn_reader(struct bufferevent *bev, void *ctx)
  148. {
  149. struct evbuffer *input = bufferevent_get_input(bev);
  150. WrkControllerConnInfo *info = (WrkControllerConnInfo *)ctx;
  151. unsigned char cmd[1];
  152. unsigned char subproblem[SUBPROBLEM_DESC_LEN];
  153. while(1) {
  154. size_t len = evbuffer_get_length(input);
  155. switch(info->state) {
  156. case WRKCCSTATE_AWAITCMD:
  157. if (len < 1) return;
  158. bufferevent_read(bev, cmd, 1);
  159. #ifdef DEBUG
  160. cerr << "Command " << cmd[0] << " received\n";
  161. #endif
  162. switch(cmd[0]) {
  163. case 'P':
  164. info->state = WRKCCSTATE_RDPROBLEM;
  165. break;
  166. case 'S':
  167. stop_working();
  168. break;
  169. default:
  170. /* Unknown command received */
  171. fprintf(stderr, "Unknown command in "
  172. "controllerconn_reader: %c\n", cmd[0]);
  173. info->state = WRKCCSTATE_END;
  174. break;
  175. }
  176. break;
  177. case WRKCCSTATE_RDPROBLEM:
  178. if (len < SUBPROBLEM_DESC_LEN+2) return;
  179. stop_working();
  180. bufferevent_read(bev, subproblem, SUBPROBLEM_DESC_LEN);
  181. bufferevent_read(bev, &(info->num_dpnodes), 2);
  182. // Careful! Subproblem uses NTL, so we must be sure
  183. // we're not multithreaded at this point.
  184. wrkctrlstate.current_problem = new Subproblem(subproblem);
  185. info->state = WRKCCSTATE_RDDPNODES;
  186. /* FALLTHROUGH */
  187. case WRKCCSTATE_RDDPNODES:
  188. if (len < 6*(info->num_dpnodes)) return;
  189. wrkctrlstate.num_expected_dpnodes = info->num_dpnodes;
  190. {
  191. unsigned short i;
  192. for(i=0;i<info->num_dpnodes;++i) {
  193. unsigned char ipport[6];
  194. bufferevent_read(bev, ipport, 6);
  195. #ifdef DEBUG
  196. cerr << "Connecting to DPnode " <<
  197. int(ipport[0]) << "." <<
  198. int(ipport[1]) << "." <<
  199. int(ipport[2]) << "." <<
  200. int(ipport[3]) << ":" <<
  201. ((ipport[4] << 8) + ipport[5]) <<
  202. "\n";
  203. #endif
  204. struct bufferevent *dpbev = client_create(
  205. bufferevent_get_base(bev), ipport,
  206. dpconn_event_cb, true);
  207. #ifdef DEBUG
  208. cerr << "Starting connection to dpnode " << dpbev << "\n";
  209. #endif
  210. if (dpbev) {
  211. wrkctrlstate.dpnodes.push_back(dpbev);
  212. } else {
  213. stop_working();
  214. }
  215. }
  216. }
  217. info->state = WRKCCSTATE_AWAITCMD;
  218. break;
  219. case WRKCCSTATE_END:
  220. // Shut down
  221. delete info;
  222. event_base_loopbreak(bufferevent_get_base(bev));
  223. #ifdef DEBUG
  224. cerr << "Closing connection to " << bev << "\n";
  225. #endif
  226. bufferevent_free(bev);
  227. return;
  228. }
  229. }
  230. }
  231. static void controllerconn_event_cb(struct bufferevent *bev, short events,
  232. void *ctx)
  233. {
  234. if (events & BEV_EVENT_CONNECTED) {
  235. // We have successfully connected to the controller
  236. char id[1] = { 'W' };
  237. bufferevent_enable(bev, EV_READ|EV_WRITE);
  238. bufferevent_write(bev, id, 1);
  239. bufferevent_setcb(bev, controllerconn_reader, NULL,
  240. controllerconn_event_cb, new WrkControllerConnInfo());
  241. } else if (events & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
  242. fprintf(stderr, "Closing connection to controller and exiting\n");
  243. event_base_loopbreak(bufferevent_get_base(bev));
  244. #ifdef DEBUG
  245. cerr << "Closing connection to " << bev << "\n";
  246. #endif
  247. bufferevent_free(bev);
  248. }
  249. }
  250. int main(int argc, char **argv)
  251. {
  252. if (argc != 3) {
  253. fprintf(stderr, "Usage: %s controller_host controller_port\n", argv[0]);
  254. return 1;
  255. }
  256. unsigned short controller_port = strtoul(argv[2], NULL, 10);
  257. evthread_use_pthreads();
  258. signal(SIGPIPE, SIG_IGN);
  259. return controller_client(argv[1], controller_port,
  260. controllerconn_event_cb, true);
  261. }