1 /*
2  * include/proto/applet.h
3  * This file contains applet function prototypes
4  *
5  * Copyright (C) 2000-2015 Willy Tarreau - w@1wt.eu
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation, version 2.1
10  * exclusively.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21 
22 #ifndef _PROTO_APPLET_H
23 #define _PROTO_APPLET_H
24 
25 #include <stdlib.h>
26 
27 #include <common/config.h>
28 #include <common/mini-clist.h>
29 #include <types/applet.h>
30 #include <proto/connection.h>
31 
32 extern unsigned int nb_applets;
33 extern unsigned long active_applets_mask;
34 extern unsigned int applets_active_queue;
35 __decl_hathreads(extern HA_SPINLOCK_T applet_active_lock);
36 extern struct list applet_active_queue;
37 
38 void applet_run_active();
39 
40 
41 static int inline appctx_res_wakeup(struct appctx *appctx);
42 
43 
44 /* Initializes all required fields for a new appctx. Note that it does the
45  * minimum acceptable initialization for an appctx. This means only the
46  * 3 integer states st0, st1, st2 are zeroed.
47  */
appctx_init(struct appctx * appctx,unsigned long thread_mask)48 static inline void appctx_init(struct appctx *appctx, unsigned long thread_mask)
49 {
50 	appctx->st0 = appctx->st1 = appctx->st2 = 0;
51 	appctx->io_release = NULL;
52 	appctx->thread_mask = thread_mask;
53 	appctx->state = APPLET_SLEEPING;
54 }
55 
56 /* Tries to allocate a new appctx and initialize its main fields. The appctx
57  * is returned on success, NULL on failure. The appctx must be released using
58  * pool_free(connection) or appctx_free(), since it's allocated from the
59  * connection pool. <applet> is assigned as the applet, but it can be NULL.
60  */
appctx_new(struct applet * applet,unsigned long thread_mask)61 static inline struct appctx *appctx_new(struct applet *applet, unsigned long thread_mask)
62 {
63 	struct appctx *appctx;
64 
65 	appctx = pool_alloc(pool_head_connection);
66 	if (likely(appctx != NULL)) {
67 		appctx->obj_type = OBJ_TYPE_APPCTX;
68 		appctx->applet = applet;
69 		appctx_init(appctx, thread_mask);
70 		LIST_INIT(&appctx->runq);
71 		LIST_INIT(&appctx->buffer_wait.list);
72 		appctx->buffer_wait.target = appctx;
73 		appctx->buffer_wait.wakeup_cb = (int (*)(void *))appctx_res_wakeup;
74 		HA_ATOMIC_ADD(&nb_applets, 1);
75 	}
76 	return appctx;
77 }
78 
79 /* Releases an appctx previously allocated by appctx_new(). Note that
80  * we share the connection pool.
81  */
__appctx_free(struct appctx * appctx)82 static inline void __appctx_free(struct appctx *appctx)
83 {
84 	if (!LIST_ISEMPTY(&appctx->runq)) {
85 		LIST_DEL(&appctx->runq);
86 		applets_active_queue--;
87 	}
88 
89 	if (!LIST_ISEMPTY(&appctx->buffer_wait.list)) {
90 		HA_SPIN_LOCK(BUF_WQ_LOCK, &buffer_wq_lock);
91 		LIST_DEL(&appctx->buffer_wait.list);
92 		LIST_INIT(&appctx->buffer_wait.list);
93 		HA_SPIN_UNLOCK(BUF_WQ_LOCK, &buffer_wq_lock);
94 	}
95 
96 	pool_free(pool_head_connection, appctx);
97 	HA_ATOMIC_SUB(&nb_applets, 1);
98 }
appctx_free(struct appctx * appctx)99 static inline void appctx_free(struct appctx *appctx)
100 {
101 	HA_SPIN_LOCK(APPLETS_LOCK, &applet_active_lock);
102 	if (appctx->state & APPLET_RUNNING) {
103 		appctx->state |= APPLET_WANT_DIE;
104 		HA_SPIN_UNLOCK(APPLETS_LOCK, &applet_active_lock);
105 		return;
106 	}
107 	__appctx_free(appctx);
108 	HA_SPIN_UNLOCK(APPLETS_LOCK, &applet_active_lock);
109 }
110 
111 /* wakes up an applet when conditions have changed */
__appctx_wakeup(struct appctx * appctx)112 static inline void __appctx_wakeup(struct appctx *appctx)
113 {
114 	if (LIST_ISEMPTY(&appctx->runq)) {
115 		LIST_ADDQ(&applet_active_queue, &appctx->runq);
116 		applets_active_queue++;
117 		active_applets_mask |= appctx->thread_mask;
118 	}
119 }
120 
appctx_wakeup(struct appctx * appctx)121 static inline void appctx_wakeup(struct appctx *appctx)
122 {
123 	HA_SPIN_LOCK(APPLETS_LOCK, &applet_active_lock);
124 	if (appctx->state & APPLET_RUNNING) {
125 		appctx->state |= APPLET_WOKEN_UP;
126 		HA_SPIN_UNLOCK(APPLETS_LOCK, &applet_active_lock);
127 		return;
128 	}
129 	__appctx_wakeup(appctx);
130 	HA_SPIN_UNLOCK(APPLETS_LOCK, &applet_active_lock);
131 }
132 
133 /* Callback used to wake up an applet when a buffer is available. The applet
134  * <appctx> is woken up is if it is not already in the list of "active"
135  * applets. This functions returns 1 is the stream is woken up, otherwise it
136  * returns 0. If task is running we request we check if woken was already
137  * requested */
appctx_res_wakeup(struct appctx * appctx)138 static inline int appctx_res_wakeup(struct appctx *appctx)
139 {
140 	HA_SPIN_LOCK(APPLETS_LOCK, &applet_active_lock);
141 	if (appctx->state & APPLET_RUNNING) {
142 		if (appctx->state & APPLET_WOKEN_UP) {
143 			HA_SPIN_UNLOCK(APPLETS_LOCK, &applet_active_lock);
144 			return 0;
145 		}
146 		appctx->state |= APPLET_WOKEN_UP;
147 		HA_SPIN_UNLOCK(APPLETS_LOCK, &applet_active_lock);
148 		return 1;
149 	}
150 	__appctx_wakeup(appctx);
151 	HA_SPIN_UNLOCK(APPLETS_LOCK, &applet_active_lock);
152 	return 1;
153 }
154 
155 
156 #endif /* _PROTO_APPLET_H */
157 
158 /*
159  * Local variables:
160  *  c-indent-level: 8
161  *  c-basic-offset: 8
162  * End:
163  */
164