1 /* 2 * Copyright 2016 Jakub Klama <jceel@FreeBSD.org> 3 * All rights reserved 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted providing that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 22 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 23 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 * POSSIBILITY OF SUCH DAMAGE. 25 * 26 */ 27 28 #ifndef LIB9P_THREADPOOL_H 29 #define LIB9P_THREADPOOL_H 30 31 #include <stdbool.h> 32 #include <pthread.h> 33 #include <sys/queue.h> 34 #include "lib9p.h" 35 36 STAILQ_HEAD(l9p_request_queue, l9p_request); 37 38 /* 39 * Most of the workers in the threadpool run requests. 40 * 41 * One distinguished worker delivers responses from the 42 * response queue. The reason this worker exists is to 43 * guarantee response order, so that flush responses go 44 * after their flushed requests. 45 */ 46 struct l9p_threadpool { 47 struct l9p_connection * ltp_conn; /* the connection */ 48 struct l9p_request_queue ltp_workq; /* requests awaiting a worker */ 49 struct l9p_request_queue ltp_replyq; /* requests that are done */ 50 pthread_mutex_t ltp_mtx; /* locks queues and cond vars */ 51 pthread_cond_t ltp_work_cv; /* to signal regular workers */ 52 pthread_cond_t ltp_reply_cv; /* to signal reply-worker */ 53 LIST_HEAD(, l9p_worker) ltp_workers; /* list of all workers */ 54 }; 55 56 /* 57 * All workers, including the responder, use this as their 58 * control structure. (The only thing that distinguishes the 59 * responder is that it runs different code and waits on the 60 * reply_cv.) 61 */ 62 struct l9p_worker { 63 struct l9p_threadpool * ltw_tp; 64 pthread_t ltw_thread; 65 bool ltw_exiting; 66 bool ltw_responder; 67 LIST_ENTRY(l9p_worker) ltw_link; 68 }; 69 70 /* 71 * Each request has a "work state" telling where the request is, 72 * in terms of workers working on it. That is, this tells us 73 * which threadpool queue, if any, the request is in now or would 74 * go in, or what's happening with it. 75 */ 76 enum l9p_workstate { 77 L9P_WS_NOTSTARTED, /* not yet started */ 78 L9P_WS_IMMEDIATE, /* Tflush being done sans worker */ 79 L9P_WS_INPROGRESS, /* worker is working on it */ 80 L9P_WS_RESPQUEUED, /* worker is done, response queued */ 81 L9P_WS_REPLYING, /* responder is in final reply path */ 82 }; 83 84 /* 85 * Each request has a "flush state", initally NONE meaning no 86 * Tflush affected the request. 87 * 88 * If a Tflush comes in before we ever assign a work thread, 89 * the flush state goes to FLUSH_REQUESTED_PRE_START. 90 * 91 * If a Tflush comes in after we assign a work thread, the 92 * flush state goes to FLUSH_REQUESTED_POST_START. The flush 93 * request may be too late: the request might finish anyway. 94 * Or it might be soon enough to abort. In all cases, though, the 95 * operation requesting the flush (the "flusher") must wait for 96 * the other request (the "flushee") to go through the respond 97 * path. The respond routine gets to decide whether to send a 98 * normal response, send an error, or drop the request 99 * entirely. 100 * 101 * There's one especially annoying case: what if a Tflush comes in 102 * *while* we're sending a response? In this case it's too late: 103 * the flush just waits for the fully-composed response. 104 */ 105 enum l9p_flushstate { 106 L9P_FLUSH_NONE = 0, /* must be zero */ 107 L9P_FLUSH_REQUESTED_PRE_START, /* not even started before flush */ 108 L9P_FLUSH_REQUESTED_POST_START, /* started, then someone said flush */ 109 L9P_FLUSH_TOOLATE /* too late, already responding */ 110 }; 111 112 void l9p_threadpool_flushee_done(struct l9p_request *); 113 int l9p_threadpool_init(struct l9p_threadpool *, int); 114 void l9p_threadpool_run(struct l9p_threadpool *, struct l9p_request *); 115 int l9p_threadpool_shutdown(struct l9p_threadpool *); 116 int l9p_threadpool_tflush(struct l9p_request *); 117 118 #endif /* LIB9P_THREADPOOL_H */ 119