Klaus Demo nginx / babd64e
Removed busy locks. Ruslan Ermilov 7 years ago
9 changed file(s) with 1 addition(s) and 802 deletion(s). Raw diff Collapse all Expand all
9191 EVENT_DEPS="src/event/ngx_event.h \
9292 src/event/ngx_event_timer.h \
9393 src/event/ngx_event_posted.h \
94 src/event/ngx_event_busy_lock.h \
9594 src/event/ngx_event_connect.h \
9695 src/event/ngx_event_pipe.h"
9796
9897 EVENT_SRCS="src/event/ngx_event.c \
9998 src/event/ngx_event_timer.c \
10099 src/event/ngx_event_posted.c \
101 src/event/ngx_event_busy_lock.c \
102100 src/event/ngx_event_accept.c \
103101 src/event/ngx_event_connect.c \
104102 src/event/ngx_event_pipe.c"
296294 src/http/ngx_http_variables.h \
297295 src/http/ngx_http_script.h \
298296 src/http/ngx_http_upstream.h \
299 src/http/ngx_http_upstream_round_robin.h \
300 src/http/ngx_http_busy_lock.h"
297 src/http/ngx_http_upstream_round_robin.h"
301298
302299 HTTP_SRCS="src/http/ngx_http.c \
303300 src/http/ngx_http_core_module.c \
321318 src/http/modules/ngx_http_headers_filter_module.c \
322319 src/http/modules/ngx_http_not_modified_filter_module.c"
323320
324 # STUB
325 HTTP_SRCS="$HTTP_SRCS src/http/ngx_http_busy_lock.c"
326
327321 HTTP_POSTPONE_FILTER_SRCS=src/http/ngx_http_postpone_filter_module.c
328322
329323 HTTP_FILE_CACHE_SRCS=src/http/ngx_http_file_cache.c
2424 } ngx_event_ovlp_t;
2525
2626 #endif
27
28
29 typedef struct {
30 ngx_uint_t lock;
31
32 ngx_event_t *events;
33 ngx_event_t *last;
34 } ngx_event_mutex_t;
3527
3628
3729 struct ngx_event_s {
532524
533525 #include <ngx_event_timer.h>
534526 #include <ngx_event_posted.h>
535 #include <ngx_event_busy_lock.h>
536527
537528 #if (NGX_WIN32)
538529 #include <ngx_iocp_module.h>
+0
-286
src/event/ngx_event_busy_lock.c less more
0
1 /*
2 * Copyright (C) Igor Sysoev
3 * Copyright (C) Nginx, Inc.
4 */
5
6
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9 #include <ngx_event.h>
10
11
12 static ngx_int_t ngx_event_busy_lock_look_cacheable(ngx_event_busy_lock_t *bl,
13 ngx_event_busy_lock_ctx_t *ctx);
14 static void ngx_event_busy_lock_handler(ngx_event_t *ev);
15 static void ngx_event_busy_lock_posted_handler(ngx_event_t *ev);
16
17
18 /*
19 * NGX_OK: the busy lock is held
20 * NGX_AGAIN: the all busy locks are held but we will wait the specified time
21 * NGX_BUSY: ctx->timer == 0: there are many the busy locks
22 * ctx->timer != 0: there are many the waiting locks
23 */
24
25 ngx_int_t
26 ngx_event_busy_lock(ngx_event_busy_lock_t *bl, ngx_event_busy_lock_ctx_t *ctx)
27 {
28 ngx_int_t rc;
29
30 ngx_mutex_lock(bl->mutex);
31
32 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->event->log, 0,
33 "event busy lock: b:%d mb:%d",
34 bl->busy, bl->max_busy);
35
36 if (bl->busy < bl->max_busy) {
37 bl->busy++;
38
39 rc = NGX_OK;
40
41 } else if (ctx->timer && bl->waiting < bl->max_waiting) {
42 bl->waiting++;
43 ngx_add_timer(ctx->event, ctx->timer);
44 ctx->event->handler = ngx_event_busy_lock_handler;
45
46 if (bl->events) {
47 bl->last->next = ctx;
48
49 } else {
50 bl->events = ctx;
51 }
52
53 bl->last = ctx;
54
55 rc = NGX_AGAIN;
56
57 } else {
58 rc = NGX_BUSY;
59 }
60
61 ngx_mutex_unlock(bl->mutex);
62
63 return rc;
64 }
65
66
67 ngx_int_t
68 ngx_event_busy_lock_cacheable(ngx_event_busy_lock_t *bl,
69 ngx_event_busy_lock_ctx_t *ctx)
70 {
71 ngx_int_t rc;
72
73 ngx_mutex_lock(bl->mutex);
74
75 rc = ngx_event_busy_lock_look_cacheable(bl, ctx);
76
77 ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ctx->event->log, 0,
78 "event busy lock: %d w:%d mw:%d",
79 rc, bl->waiting, bl->max_waiting);
80
81 /*
82 * NGX_OK: no the same request, there is free slot and we locked it
83 * NGX_BUSY: no the same request and there is no free slot
84 * NGX_AGAIN: the same request is processing
85 */
86
87 if (rc == NGX_AGAIN) {
88
89 if (ctx->timer && bl->waiting < bl->max_waiting) {
90 bl->waiting++;
91 ngx_add_timer(ctx->event, ctx->timer);
92 ctx->event->handler = ngx_event_busy_lock_handler;
93
94 if (bl->events == NULL) {
95 bl->events = ctx;
96 } else {
97 bl->last->next = ctx;
98 }
99 bl->last = ctx;
100
101 } else {
102 rc = NGX_BUSY;
103 }
104 }
105
106 ngx_mutex_unlock(bl->mutex);
107
108 return rc;
109 }
110
111
112 void
113 ngx_event_busy_unlock(ngx_event_busy_lock_t *bl,
114 ngx_event_busy_lock_ctx_t *ctx)
115 {
116 ngx_event_t *ev;
117 ngx_event_busy_lock_ctx_t *wakeup;
118
119 ngx_mutex_lock(bl->mutex);
120
121 if (bl->events) {
122 wakeup = bl->events;
123 bl->events = bl->events->next;
124
125 } else {
126 wakeup = NULL;
127 bl->busy--;
128 }
129
130 /*
131 * MP: all ctx's and their queue must be in shared memory,
132 * each ctx has pid to wake up
133 */
134
135 if (wakeup == NULL) {
136 ngx_mutex_unlock(bl->mutex);
137 return;
138 }
139
140 if (ctx->md5) {
141 for (wakeup = bl->events; wakeup; wakeup = wakeup->next) {
142 if (wakeup->md5 == NULL || wakeup->slot != ctx->slot) {
143 continue;
144 }
145
146 wakeup->handler = ngx_event_busy_lock_posted_handler;
147 wakeup->cache_updated = 1;
148
149 ev = wakeup->event;
150
151 ngx_post_event(ev, &ngx_posted_events);
152 }
153
154 ngx_mutex_unlock(bl->mutex);
155
156 } else {
157 bl->waiting--;
158
159 ngx_mutex_unlock(bl->mutex);
160
161 wakeup->handler = ngx_event_busy_lock_posted_handler;
162 wakeup->locked = 1;
163
164 ev = wakeup->event;
165
166 if (ev->timer_set) {
167 ngx_del_timer(ev);
168 }
169
170 ngx_post_event(ev, &ngx_posted_events);
171 }
172 }
173
174
175 void
176 ngx_event_busy_lock_cancel(ngx_event_busy_lock_t *bl,
177 ngx_event_busy_lock_ctx_t *ctx)
178 {
179 ngx_event_busy_lock_ctx_t *c, *p;
180
181 ngx_mutex_lock(bl->mutex);
182
183 bl->waiting--;
184
185 if (ctx == bl->events) {
186 bl->events = ctx->next;
187
188 } else {
189 p = bl->events;
190 for (c = bl->events->next; c; c = c->next) {
191 if (c == ctx) {
192 p->next = ctx->next;
193 break;
194 }
195 p = c;
196 }
197 }
198
199 ngx_mutex_unlock(bl->mutex);
200 }
201
202
203 static ngx_int_t
204 ngx_event_busy_lock_look_cacheable(ngx_event_busy_lock_t *bl,
205 ngx_event_busy_lock_ctx_t *ctx)
206 {
207 ngx_int_t free;
208 ngx_uint_t i, bit, cacheable, mask;
209
210 bit = 0;
211 cacheable = 0;
212 free = -1;
213
214 #if (NGX_SUPPRESS_WARN)
215 mask = 0;
216 #endif
217
218 for (i = 0; i < bl->max_busy; i++) {
219
220 if ((bit & 7) == 0) {
221 mask = bl->md5_mask[i / 8];
222 }
223
224 if (mask & 1) {
225 if (ngx_memcmp(&bl->md5[i * 16], ctx->md5, 16) == 0) {
226 ctx->waiting = 1;
227 ctx->slot = i;
228 return NGX_AGAIN;
229 }
230 cacheable++;
231
232 } else if (free == -1) {
233 free = i;
234 }
235
236 if (cacheable == bl->cacheable) {
237 if (free == -1 && cacheable < bl->max_busy) {
238 free = i + 1;
239 }
240
241 break;
242 }
243
244 mask >>= 1;
245 bit++;
246 }
247
248 if (free == -1) {
249 return NGX_BUSY;
250 }
251
252 #if 0
253 if (bl->busy == bl->max_busy) {
254 return NGX_BUSY;
255 }
256 #endif
257
258 ngx_memcpy(&bl->md5[free * 16], ctx->md5, 16);
259 bl->md5_mask[free / 8] |= 1 << (free & 7);
260 ctx->slot = free;
261
262 bl->cacheable++;
263 bl->busy++;
264
265 return NGX_OK;
266 }
267
268
269 static void
270 ngx_event_busy_lock_handler(ngx_event_t *ev)
271 {
272 ev->handler = ngx_event_busy_lock_posted_handler;
273
274 ngx_post_event(ev, &ngx_posted_events);
275 }
276
277
278 static void
279 ngx_event_busy_lock_posted_handler(ngx_event_t *ev)
280 {
281 ngx_event_busy_lock_ctx_t *ctx;
282
283 ctx = ev->data;
284 ctx->handler(ev);
285 }
+0
-65
src/event/ngx_event_busy_lock.h less more
0
1 /*
2 * Copyright (C) Igor Sysoev
3 * Copyright (C) Nginx, Inc.
4 */
5
6
7 #ifndef _NGX_EVENT_BUSY_LOCK_H_INCLUDED_
8 #define _NGX_EVENT_BUSY_LOCK_H_INCLUDED_
9
10
11 #include <ngx_config.h>
12 #include <ngx_core.h>
13 #include <ngx_event.h>
14
15 typedef struct ngx_event_busy_lock_ctx_s ngx_event_busy_lock_ctx_t;
16
17 struct ngx_event_busy_lock_ctx_s {
18 ngx_event_t *event;
19 ngx_event_handler_pt handler;
20 void *data;
21 ngx_msec_t timer;
22
23 unsigned locked:1;
24 unsigned waiting:1;
25 unsigned cache_updated:1;
26
27 char *md5;
28 ngx_int_t slot;
29
30 ngx_event_busy_lock_ctx_t *next;
31 };
32
33
34 typedef struct {
35 u_char *md5_mask;
36 char *md5;
37 ngx_uint_t cacheable;
38
39 ngx_uint_t busy;
40 ngx_uint_t max_busy;
41
42 ngx_uint_t waiting;
43 ngx_uint_t max_waiting;
44
45 ngx_event_busy_lock_ctx_t *events;
46 ngx_event_busy_lock_ctx_t *last;
47
48 #if (NGX_OLD_THREADS)
49 ngx_mutex_t *mutex;
50 #endif
51 } ngx_event_busy_lock_t;
52
53
54 ngx_int_t ngx_event_busy_lock(ngx_event_busy_lock_t *bl,
55 ngx_event_busy_lock_ctx_t *ctx);
56 ngx_int_t ngx_event_busy_lock_cacheable(ngx_event_busy_lock_t *bl,
57 ngx_event_busy_lock_ctx_t *ctx);
58 void ngx_event_busy_unlock(ngx_event_busy_lock_t *bl,
59 ngx_event_busy_lock_ctx_t *ctx);
60 void ngx_event_busy_lock_cancel(ngx_event_busy_lock_t *bl,
61 ngx_event_busy_lock_ctx_t *ctx);
62
63
64 #endif /* _NGX_EVENT_BUSY_LOCK_H_INCLUDED_ */
+0
-70
src/event/ngx_event_mutex.c less more
0
1 /*
2 * Copyright (C) Igor Sysoev
3 * Copyright (C) Nginx, Inc.
4 */
5
6
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9 #include <ngx_event.h>
10
11
12 ngx_int_t ngx_event_mutex_timedlock(ngx_event_mutex_t *m, ngx_msec_t timer,
13 ngx_event_t *ev)
14 {
15 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
16 "lock event mutex %p lock:%XD", m, m->lock);
17
18 if (m->lock) {
19
20 if (m->events == NULL) {
21 m->events = ev;
22
23 } else {
24 m->last->next = ev;
25 }
26
27 m->last = ev;
28 ev->next = NULL;
29
30 #if (NGX_OLD_THREADS0)
31 ev->light = 1;
32 #endif
33
34 ngx_add_timer(ev, timer);
35
36 return NGX_AGAIN;
37 }
38
39 m->lock = 1;
40
41 return NGX_OK;
42 }
43
44
45 ngx_int_t ngx_event_mutex_unlock(ngx_event_mutex_t *m, ngx_log_t *log)
46 {
47 ngx_event_t *ev;
48
49 if (m->lock == 0) {
50 ngx_log_error(NGX_LOG_ALERT, log, 0,
51 "tring to unlock the free event mutex %p", m);
52 return NGX_ERROR;
53 }
54
55 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0,
56 "unlock event mutex %p, next event: %p", m, m->events);
57
58 m->lock = 0;
59
60 if (m->events) {
61 ev = m->events;
62 m->events = ev->next;
63
64 ev->next = ngx_posted_events;
65 ngx_posted_events = ev;
66 }
67
68 return NGX_OK;
69 }
3535 #include <ngx_http_script.h>
3636 #include <ngx_http_upstream.h>
3737 #include <ngx_http_upstream_round_robin.h>
38 #include <ngx_http_busy_lock.h>
3938 #include <ngx_http_core_module.h>
4039
4140 #if (NGX_HTTP_SPDY)
+0
-307
src/http/ngx_http_busy_lock.c less more
0
1 /*
2 * Copyright (C) Igor Sysoev
3 * Copyright (C) Nginx, Inc.
4 */
5
6
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9 #include <ngx_http.h>
10
11
12
13 static int ngx_http_busy_lock_look_cacheable(ngx_http_busy_lock_t *bl,
14 ngx_http_busy_lock_ctx_t *bc,
15 int lock);
16
17
18 int ngx_http_busy_lock(ngx_http_busy_lock_t *bl, ngx_http_busy_lock_ctx_t *bc)
19 {
20 if (bl->busy < bl->max_busy) {
21 bl->busy++;
22
23 if (bc->time) {
24 bc->time = 0;
25 bl->waiting--;
26 }
27
28 return NGX_OK;
29 }
30
31 if (bc->time) {
32 if (bc->time < bl->timeout) {
33 ngx_add_timer(bc->event, 1000);
34 return NGX_AGAIN;
35 }
36
37 bl->waiting--;
38 return NGX_DONE;
39
40 }
41
42 if (bl->timeout == 0) {
43 return NGX_DONE;
44 }
45
46 if (bl->waiting < bl->max_waiting) {
47 bl->waiting++;
48
49 #if 0
50 ngx_add_timer(bc->event, 1000);
51 bc->event->event_handler = bc->event_handler;
52 #endif
53
54 /* TODO: ngx_handle_level_read_event() */
55
56 return NGX_AGAIN;
57 }
58
59 return NGX_ERROR;
60 }
61
62
63 int ngx_http_busy_lock_cacheable(ngx_http_busy_lock_t *bl,
64 ngx_http_busy_lock_ctx_t *bc, int lock)
65 {
66 int rc;
67
68 rc = ngx_http_busy_lock_look_cacheable(bl, bc, lock);
69
70 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, bc->event->log, 0,
71 "http busylock: %d w:%d mw::%d",
72 rc, bl->waiting, bl->max_waiting);
73
74 if (rc == NGX_OK) { /* no the same request, there's free slot */
75 return NGX_OK;
76 }
77
78 if (rc == NGX_ERROR && !lock) { /* no the same request, no free slot */
79 return NGX_OK;
80 }
81
82 /* rc == NGX_AGAIN: the same request */
83
84 if (bc->time) {
85 if (bc->time < bl->timeout) {
86 ngx_add_timer(bc->event, 1000);
87 return NGX_AGAIN;
88 }
89
90 bl->waiting--;
91 return NGX_DONE;
92
93 }
94
95 if (bl->timeout == 0) {
96 return NGX_DONE;
97 }
98
99 if (bl->waiting < bl->max_waiting) {
100 #if 0
101 bl->waiting++;
102 ngx_add_timer(bc->event, 1000);
103 bc->event->event_handler = bc->event_handler;
104 #endif
105
106 /* TODO: ngx_handle_level_read_event() */
107
108 return NGX_AGAIN;
109 }
110
111 return NGX_ERROR;
112 }
113
114
115 void ngx_http_busy_unlock(ngx_http_busy_lock_t *bl,
116 ngx_http_busy_lock_ctx_t *bc)
117 {
118 if (bl == NULL) {
119 return;
120 }
121
122 if (bl->md5) {
123 bl->md5_mask[bc->slot / 8] &= ~(1 << (bc->slot & 7));
124 bl->cacheable--;
125 }
126
127 bl->busy--;
128 }
129
130
131 static int ngx_http_busy_lock_look_cacheable(ngx_http_busy_lock_t *bl,
132 ngx_http_busy_lock_ctx_t *bc,
133 int lock)
134 {
135 int i, b, cacheable, free;
136 u_int mask;
137
138 b = 0;
139 cacheable = 0;
140 free = -1;
141
142 #if (NGX_SUPPRESS_WARN)
143 mask = 0;
144 #endif
145
146 for (i = 0; i < bl->max_busy; i++) {
147
148 if ((b & 7) == 0) {
149 mask = bl->md5_mask[i / 8];
150 }
151
152 if (mask & 1) {
153 if (ngx_memcmp(&bl->md5[i * 16], bc->md5, 16) == 0) {
154 return NGX_AGAIN;
155 }
156 cacheable++;
157
158 } else if (free == -1) {
159 free = i;
160 }
161
162 #if 1
163 if (cacheable == bl->cacheable) {
164 if (free == -1 && cacheable < bl->max_busy) {
165 free = i + 1;
166 }
167
168 break;
169 }
170 #endif
171
172 mask >>= 1;
173 b++;
174 }
175
176 if (free == -1) {
177 return NGX_ERROR;
178 }
179
180 if (lock) {
181 if (bl->busy == bl->max_busy) {
182 return NGX_ERROR;
183 }
184
185 ngx_memcpy(&bl->md5[free * 16], bc->md5, 16);
186 bl->md5_mask[free / 8] |= 1 << (free & 7);
187 bc->slot = free;
188
189 bl->cacheable++;
190 bl->busy++;
191 }
192
193 return NGX_OK;
194 }
195
196
197 char *ngx_http_set_busy_lock_slot(ngx_conf_t *cf, ngx_command_t *cmd,
198 void *conf)
199 {
200 char *p = conf;
201
202 ngx_uint_t i, dup, invalid;
203 ngx_str_t *value, line;
204 ngx_http_busy_lock_t *bl, **blp;
205
206 blp = (ngx_http_busy_lock_t **) (p + cmd->offset);
207 if (*blp) {
208 return "is duplicate";
209 }
210
211 /* ngx_calloc_shared() */
212 bl = ngx_pcalloc(cf->pool, sizeof(ngx_http_busy_lock_t));
213 if (bl == NULL) {
214 return NGX_CONF_ERROR;
215 }
216 *blp = bl;
217
218 /* ngx_calloc_shared() */
219 bl->mutex = ngx_pcalloc(cf->pool, sizeof(ngx_event_mutex_t));
220 if (bl->mutex == NULL) {
221 return NGX_CONF_ERROR;
222 }
223
224 dup = 0;
225 invalid = 0;
226 value = cf->args->elts;
227
228 for (i = 1; i < cf->args->nelts; i++) {
229
230 if (value[i].data[1] != '=') {
231 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
232 "invalid value \"%s\"", value[i].data);
233 return NGX_CONF_ERROR;
234 }
235
236 switch (value[i].data[0]) {
237
238 case 'b':
239 if (bl->max_busy) {
240 dup = 1;
241 break;
242 }
243
244 bl->max_busy = ngx_atoi(value[i].data + 2, value[i].len - 2);
245 if (bl->max_busy == NGX_ERROR) {
246 invalid = 1;
247 break;
248 }
249
250 continue;
251
252 case 'w':
253 if (bl->max_waiting) {
254 dup = 1;
255 break;
256 }
257
258 bl->max_waiting = ngx_atoi(value[i].data + 2, value[i].len - 2);
259 if (bl->max_waiting == NGX_ERROR) {
260 invalid = 1;
261 break;
262 }
263
264 continue;
265
266 case 't':
267 if (bl->timeout) {
268 dup = 1;
269 break;
270 }
271
272 line.len = value[i].len - 2;
273 line.data = value[i].data + 2;
274
275 bl->timeout = ngx_parse_time(&line, 1);
276 if (bl->timeout == (time_t) NGX_ERROR) {
277 invalid = 1;
278 break;
279 }
280
281 continue;
282
283 default:
284 invalid = 1;
285 }
286
287 if (dup) {
288 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
289 "duplicate value \"%s\"", value[i].data);
290 return NGX_CONF_ERROR;
291 }
292
293 if (invalid) {
294 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
295 "invalid value \"%s\"", value[i].data);
296 return NGX_CONF_ERROR;
297 }
298 }
299
300 if (bl->timeout == 0 && bl->max_waiting) {
301 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
302 "busy lock waiting is useless with zero timeout, ignoring");
303 }
304
305 return NGX_CONF_OK;
306 }
+0
-54
src/http/ngx_http_busy_lock.h less more
0
1 /*
2 * Copyright (C) Igor Sysoev
3 * Copyright (C) Nginx, Inc.
4 */
5
6
7 #ifndef _NGX_HTTP_BUSY_LOCK_H_INCLUDED_
8 #define _NGX_HTTP_BUSY_LOCK_H_INCLUDED_
9
10
11 #include <ngx_config.h>
12 #include <ngx_core.h>
13 #include <ngx_event.h>
14 #include <ngx_http.h>
15
16
17 typedef struct {
18 u_char *md5_mask;
19 char *md5;
20 int cacheable;
21
22 int busy;
23 int max_busy;
24
25 int waiting;
26 int max_waiting;
27
28 time_t timeout;
29
30 ngx_event_mutex_t *mutex;
31 } ngx_http_busy_lock_t;
32
33
34 typedef struct {
35 time_t time;
36 ngx_event_t *event;
37 void (*event_handler)(ngx_event_t *ev);
38 u_char *md5;
39 int slot;
40 } ngx_http_busy_lock_ctx_t;
41
42
43 int ngx_http_busy_lock(ngx_http_busy_lock_t *bl, ngx_http_busy_lock_ctx_t *bc);
44 int ngx_http_busy_lock_cacheable(ngx_http_busy_lock_t *bl,
45 ngx_http_busy_lock_ctx_t *bc, int lock);
46 void ngx_http_busy_unlock(ngx_http_busy_lock_t *bl,
47 ngx_http_busy_lock_ctx_t *bc);
48
49 char *ngx_http_set_busy_lock_slot(ngx_conf_t *cf, ngx_command_t *cmd,
50 void *conf);
51
52
53 #endif /* _NGX_HTTP_BUSY_LOCK_H_INCLUDED_ */
6666
6767 #endif
6868
69 #define ngx_mutex_lock(m)
70 #define ngx_mutex_unlock(m)
71
7269
7370 #endif /* _NGX_THREAD_H_INCLUDED_ */