Merge remote-tracking branch 'origin/stable-2.0'
[bpt/guile.git] / libguile / fluids.c
1 /* Copyright (C) 1996,1997,2000,2001, 2004, 2006, 2007, 2008, 2009, 2010,
2 * 2011, 2012, 2013 Free Software Foundation, Inc.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public License
6 * as published by the Free Software Foundation; either version 3 of
7 * the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301 USA
18 */
19
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23
24 #include <stdio.h>
25 #include <string.h>
26
27 #include "libguile/_scm.h"
28 #include "libguile/print.h"
29 #include "libguile/dynwind.h"
30 #include "libguile/fluids.h"
31 #include "libguile/alist.h"
32 #include "libguile/eval.h"
33 #include "libguile/ports.h"
34 #include "libguile/deprecation.h"
35 #include "libguile/validate.h"
36 #include "libguile/bdw-gc.h"
37
38 /* Number of additional slots to allocate when ALLOCATED_FLUIDS is full. */
39 #define FLUID_GROW 128
40
41 /* Vector of allocated fluids indexed by fluid numbers. Access is protected by
42 FLUID_ADMIN_MUTEX. */
43 static void **allocated_fluids = NULL;
44 static size_t allocated_fluids_len = 0;
45
46 static scm_i_pthread_mutex_t fluid_admin_mutex = SCM_I_PTHREAD_MUTEX_INITIALIZER;
47
48 #define IS_FLUID(x) SCM_FLUID_P (x)
49 #define FLUID_NUM(x) SCM_I_FLUID_NUM (x)
50
51 #define IS_DYNAMIC_STATE(x) SCM_I_DYNAMIC_STATE_P (x)
52 #define DYNAMIC_STATE_FLUIDS(x) SCM_I_DYNAMIC_STATE_FLUIDS (x)
53 #define SET_DYNAMIC_STATE_FLUIDS(x, y) SCM_SET_CELL_WORD_1 ((x), (SCM_UNPACK (y)))
54
55
56 \f
57 /* Grow STATE so that it can hold up to ALLOCATED_FLUIDS_LEN fluids. This may
58 be more than necessary since ALLOCATED_FLUIDS is sparse and the current
59 thread may not access all the fluids anyway. Memory usage could be improved
60 by using a 2-level array as is done in glibc for pthread keys (TODO). */
61 static void
62 grow_dynamic_state (SCM state)
63 {
64 SCM new_fluids;
65 SCM old_fluids = DYNAMIC_STATE_FLUIDS (state);
66 size_t i, len, old_len = SCM_SIMPLE_VECTOR_LENGTH (old_fluids);
67
68 /* Assume the assignment below is atomic. */
69 len = allocated_fluids_len;
70
71 new_fluids = scm_c_make_vector (len, SCM_UNDEFINED);
72
73 for (i = 0; i < old_len; i++)
74 SCM_SIMPLE_VECTOR_SET (new_fluids, i,
75 SCM_SIMPLE_VECTOR_REF (old_fluids, i));
76 SET_DYNAMIC_STATE_FLUIDS (state, new_fluids);
77 }
78
79 void
80 scm_i_fluid_print (SCM exp, SCM port, scm_print_state *pstate SCM_UNUSED)
81 {
82 scm_puts_unlocked ("#<fluid ", port);
83 scm_intprint ((int) FLUID_NUM (exp), 10, port);
84 scm_putc_unlocked ('>', port);
85 }
86
87 void
88 scm_i_dynamic_state_print (SCM exp, SCM port, scm_print_state *pstate SCM_UNUSED)
89 {
90 scm_puts_unlocked ("#<dynamic-state ", port);
91 scm_intprint (SCM_UNPACK (exp), 16, port);
92 scm_putc_unlocked ('>', port);
93 }
94
95 \f
96 /* Return a new fluid. */
97 static SCM
98 new_fluid (SCM init)
99 {
100 SCM fluid;
101 size_t trial, n;
102
103 /* Fluids hold the type tag and the fluid number in the first word,
104 and the default value in the second word. */
105 fluid = scm_cell (scm_tc7_fluid, SCM_UNPACK (init));
106 SCM_SET_CELL_TYPE (fluid, scm_tc7_fluid);
107
108 scm_dynwind_begin (0);
109 scm_i_dynwind_pthread_mutex_lock (&fluid_admin_mutex);
110
111 for (trial = 0; trial < 2; trial++)
112 {
113 /* Look for a free fluid number. */
114 for (n = 0; n < allocated_fluids_len; n++)
115 /* TODO: Use `__sync_bool_compare_and_swap' where available. */
116 if (allocated_fluids[n] == NULL)
117 break;
118
119 if (trial == 0 && n >= allocated_fluids_len)
120 /* All fluid numbers are in use. Run a GC and retry. Explicitly
121 running the GC is costly and bad-style. We only do this because
122 dynamic state fluid vectors would grow unreasonably if fluid numbers
123 weren't reused. */
124 scm_i_gc ("fluids");
125 }
126
127 if (n >= allocated_fluids_len)
128 {
129 /* Grow the vector of allocated fluids. */
130 void **new_allocated_fluids =
131 scm_gc_malloc_pointerless ((allocated_fluids_len + FLUID_GROW)
132 * sizeof (*allocated_fluids),
133 "allocated fluids");
134
135 /* Copy over old values and initialize rest. GC can not run
136 during these two operations since there is no safe point in
137 them. */
138 memcpy (new_allocated_fluids, allocated_fluids,
139 allocated_fluids_len * sizeof (*allocated_fluids));
140 memset (new_allocated_fluids + allocated_fluids_len, 0,
141 FLUID_GROW * sizeof (*allocated_fluids));
142 n = allocated_fluids_len;
143
144 /* Update the vector of allocated fluids. Dynamic states will
145 eventually be lazily grown to accomodate the new value of
146 ALLOCATED_FLUIDS_LEN in `fluid-ref' and `fluid-set!'. */
147 allocated_fluids = new_allocated_fluids;
148 allocated_fluids_len += FLUID_GROW;
149 }
150
151 allocated_fluids[n] = SCM_UNPACK_POINTER (fluid);
152 SCM_SET_CELL_WORD_0 (fluid, (scm_tc7_fluid | (n << 8)));
153
154 GC_GENERAL_REGISTER_DISAPPEARING_LINK (&allocated_fluids[n],
155 SCM2PTR (fluid));
156
157 scm_dynwind_end ();
158
159 /* Now null out values. We could (and probably should) do this when
160 the fluid is collected instead of now. */
161 scm_i_reset_fluid (n);
162
163 return fluid;
164 }
165
166 SCM
167 scm_make_fluid (void)
168 {
169 return new_fluid (SCM_BOOL_F);
170 }
171
172 SCM_DEFINE (scm_make_fluid_with_default, "make-fluid", 0, 1, 0,
173 (SCM dflt),
174 "Return a newly created fluid, whose initial value is @var{dflt},\n"
175 "or @code{#f} if @var{dflt} is not given.\n"
176 "Fluids are objects that can hold one\n"
177 "value per dynamic state. That is, modifications to this value are\n"
178 "only visible to code that executes with the same dynamic state as\n"
179 "the modifying code. When a new dynamic state is constructed, it\n"
180 "inherits the values from its parent. Because each thread normally executes\n"
181 "with its own dynamic state, you can use fluids for thread local storage.")
182 #define FUNC_NAME s_scm_make_fluid_with_default
183 {
184 return new_fluid (SCM_UNBNDP (dflt) ? SCM_BOOL_F : dflt);
185 }
186 #undef FUNC_NAME
187
188 SCM_DEFINE (scm_make_unbound_fluid, "make-unbound-fluid", 0, 0, 0,
189 (),
190 "Make a fluid that is initially unbound.")
191 #define FUNC_NAME s_scm_make_unbound_fluid
192 {
193 return new_fluid (SCM_UNDEFINED);
194 }
195 #undef FUNC_NAME
196
197 SCM_DEFINE (scm_fluid_p, "fluid?", 1, 0, 0,
198 (SCM obj),
199 "Return @code{#t} iff @var{obj} is a fluid; otherwise, return\n"
200 "@code{#f}.")
201 #define FUNC_NAME s_scm_fluid_p
202 {
203 return scm_from_bool (IS_FLUID (obj));
204 }
205 #undef FUNC_NAME
206
207 int
208 scm_is_fluid (SCM obj)
209 {
210 return IS_FLUID (obj);
211 }
212
213 /* Does not check type of `fluid'! */
214 static SCM
215 fluid_ref (SCM fluid)
216 {
217 SCM ret;
218 SCM fluids = DYNAMIC_STATE_FLUIDS (SCM_I_CURRENT_THREAD->dynamic_state);
219
220 if (SCM_UNLIKELY (FLUID_NUM (fluid) >= SCM_SIMPLE_VECTOR_LENGTH (fluids)))
221 {
222 /* Lazily grow the current thread's dynamic state. */
223 grow_dynamic_state (SCM_I_CURRENT_THREAD->dynamic_state);
224
225 fluids = DYNAMIC_STATE_FLUIDS (SCM_I_CURRENT_THREAD->dynamic_state);
226 }
227
228 ret = SCM_SIMPLE_VECTOR_REF (fluids, FLUID_NUM (fluid));
229 if (SCM_UNBNDP (ret))
230 return SCM_I_FLUID_DEFAULT (fluid);
231 else
232 return ret;
233 }
234
235 SCM_DEFINE (scm_fluid_ref, "fluid-ref", 1, 0, 0,
236 (SCM fluid),
237 "Return the value associated with @var{fluid} in the current\n"
238 "dynamic root. If @var{fluid} has not been set, then return\n"
239 "@code{#f}.")
240 #define FUNC_NAME s_scm_fluid_ref
241 {
242 SCM val;
243 SCM_VALIDATE_FLUID (1, fluid);
244 val = fluid_ref (fluid);
245 if (SCM_UNBNDP (val))
246 SCM_MISC_ERROR ("unbound fluid: ~S",
247 scm_list_1 (fluid));
248 return val;
249 }
250 #undef FUNC_NAME
251
252 SCM_DEFINE (scm_fluid_set_x, "fluid-set!", 2, 0, 0,
253 (SCM fluid, SCM value),
254 "Set the value associated with @var{fluid} in the current dynamic root.")
255 #define FUNC_NAME s_scm_fluid_set_x
256 {
257 SCM fluids = DYNAMIC_STATE_FLUIDS (SCM_I_CURRENT_THREAD->dynamic_state);
258
259 SCM_VALIDATE_FLUID (1, fluid);
260
261 if (SCM_UNLIKELY (FLUID_NUM (fluid) >= SCM_SIMPLE_VECTOR_LENGTH (fluids)))
262 {
263 /* Lazily grow the current thread's dynamic state. */
264 grow_dynamic_state (SCM_I_CURRENT_THREAD->dynamic_state);
265
266 fluids = DYNAMIC_STATE_FLUIDS (SCM_I_CURRENT_THREAD->dynamic_state);
267 }
268
269 SCM_SIMPLE_VECTOR_SET (fluids, FLUID_NUM (fluid), value);
270 return SCM_UNSPECIFIED;
271 }
272 #undef FUNC_NAME
273
274 SCM_DEFINE (scm_fluid_unset_x, "fluid-unset!", 1, 0, 0,
275 (SCM fluid),
276 "Unset the value associated with @var{fluid}.")
277 #define FUNC_NAME s_scm_fluid_unset_x
278 {
279 /* FIXME: really unset the default value, too? The current test
280 suite demands it, but I would prefer not to. */
281 SCM_SET_CELL_OBJECT_1 (fluid, SCM_UNDEFINED);
282 return scm_fluid_set_x (fluid, SCM_UNDEFINED);
283 }
284 #undef FUNC_NAME
285
286 SCM_DEFINE (scm_fluid_bound_p, "fluid-bound?", 1, 0, 0,
287 (SCM fluid),
288 "Return @code{#t} iff @var{fluid} is bound to a value.\n"
289 "Throw an error if @var{fluid} is not a fluid.")
290 #define FUNC_NAME s_scm_fluid_bound_p
291 {
292 SCM val;
293 SCM_VALIDATE_FLUID (1, fluid);
294 val = fluid_ref (fluid);
295 return scm_from_bool (! (SCM_UNBNDP (val)));
296 }
297 #undef FUNC_NAME
298
299 static SCM
300 apply_thunk (void *thunk)
301 {
302 return scm_call_0 (SCM_PACK (thunk));
303 }
304
305 size_t
306 scm_prepare_fluids (size_t n, SCM *fluids, SCM *values)
307 {
308 size_t j;
309
310 /* Ensure that there are no duplicates in the fluids set -- an N^2 operation,
311 but N will usually be small, so perhaps that's OK. */
312 for (j = n; j--;)
313 {
314 size_t i;
315
316 if (SCM_UNLIKELY (!IS_FLUID (fluids[j])))
317 scm_wrong_type_arg ("with-fluids", 0, fluids[j]);
318
319 for (i = j; i--;)
320 if (scm_is_eq (fluids[i], fluids[j]))
321 {
322 values[i] = values[j]; /* later bindings win */
323 n--;
324 fluids[j] = fluids[n];
325 values[j] = values[n];
326 break;
327 }
328 }
329
330 return n;
331 }
332
333 void
334 scm_swap_fluids (size_t n, SCM *fluids, SCM *values, SCM dynstate)
335 {
336 SCM fluid_vector;
337 size_t i, max = 0;
338
339 fluid_vector = DYNAMIC_STATE_FLUIDS (dynstate);
340
341 /* We could cache the max in the with-fluids, but that would take more mem,
342 and we're touching all the fluids anyway, so this per-swap traversal should
343 be OK. */
344 for (i = 0; i < n; i++)
345 {
346 size_t num = FLUID_NUM (fluids[i]);
347 max = (max > num) ? max : num;
348 }
349
350 if (SCM_UNLIKELY (max >= SCM_SIMPLE_VECTOR_LENGTH (fluid_vector)))
351 {
352 /* Lazily grow the current thread's dynamic state. */
353 grow_dynamic_state (dynstate);
354
355 fluid_vector = DYNAMIC_STATE_FLUIDS (dynstate);
356 }
357
358 /* Bind the fluids. Order doesn't matter, as all fluids are distinct. */
359 for (i = 0; i < n; i++)
360 {
361 size_t fluid_num;
362 SCM x;
363
364 fluid_num = FLUID_NUM (fluids[i]);
365 x = SCM_SIMPLE_VECTOR_REF (fluid_vector, fluid_num);
366 SCM_SIMPLE_VECTOR_SET (fluid_vector, fluid_num, values[i]);
367 values[i] = x;
368 }
369 }
370
371 SCM_DEFINE (scm_with_fluids, "with-fluids*", 3, 0, 0,
372 (SCM fluids, SCM values, SCM thunk),
373 "Set @var{fluids} to @var{values} temporary, and call @var{thunk}.\n"
374 "@var{fluids} must be a list of fluids and @var{values} must be the same\n"
375 "number of their values to be applied. Each substitution is done\n"
376 "one after another. @var{thunk} must be a procedure with no argument.")
377 #define FUNC_NAME s_scm_with_fluids
378 {
379 return scm_c_with_fluids (fluids, values,
380 apply_thunk, (void *) SCM_UNPACK (thunk));
381 }
382 #undef FUNC_NAME
383
384 SCM
385 scm_c_with_fluids (SCM fluids, SCM values, SCM (*cproc) (), void *cdata)
386 #define FUNC_NAME "scm_c_with_fluids"
387 {
388 SCM ans;
389 long flen, vlen, i;
390 SCM *fluidsv, *valuesv;
391 scm_i_thread *thread = SCM_I_CURRENT_THREAD;
392
393 SCM_VALIDATE_LIST_COPYLEN (1, fluids, flen);
394 SCM_VALIDATE_LIST_COPYLEN (2, values, vlen);
395 if (flen != vlen)
396 scm_out_of_range (s_scm_with_fluids, values);
397
398 if (SCM_UNLIKELY (flen == 0))
399 return cproc (cdata);
400
401 fluidsv = alloca (sizeof(SCM)*flen);
402 valuesv = alloca (sizeof(SCM)*flen);
403
404 for (i = 0; i < flen; i++)
405 {
406 fluidsv[i] = SCM_CAR (fluids);
407 fluids = SCM_CDR (fluids);
408 valuesv[i] = SCM_CAR (values);
409 values = SCM_CDR (values);
410 }
411
412 scm_dynstack_push_fluids (&thread->dynstack, flen, fluidsv, valuesv,
413 thread->dynamic_state);
414 ans = cproc (cdata);
415 scm_dynstack_unwind_fluids (&thread->dynstack, thread->dynamic_state);
416
417 return ans;
418 }
419 #undef FUNC_NAME
420
421 SCM_DEFINE (scm_with_fluid, "with-fluid*", 3, 0, 0,
422 (SCM fluid, SCM value, SCM thunk),
423 "Set @var{fluid} to @var{value} temporarily, and call @var{thunk}.\n"
424 "@var{thunk} must be a procedure with no argument.")
425 #define FUNC_NAME s_scm_with_fluid
426 {
427 return scm_c_with_fluid (fluid, value,
428 apply_thunk, (void *) SCM_UNPACK (thunk));
429 }
430 #undef FUNC_NAME
431
432 SCM
433 scm_c_with_fluid (SCM fluid, SCM value, SCM (*cproc) (), void *cdata)
434 #define FUNC_NAME "scm_c_with_fluid"
435 {
436 SCM ans;
437 scm_i_thread *thread = SCM_I_CURRENT_THREAD;
438
439 scm_dynstack_push_fluids (&thread->dynstack, 1, &fluid, &value,
440 thread->dynamic_state);
441 ans = cproc (cdata);
442 scm_dynstack_unwind_fluids (&thread->dynstack, thread->dynamic_state);
443
444 return ans;
445 }
446 #undef FUNC_NAME
447
448 static void
449 swap_fluid (SCM data)
450 {
451 SCM f = SCM_CAR (data);
452 SCM t = fluid_ref (f);
453 scm_fluid_set_x (f, SCM_CDR (data));
454 SCM_SETCDR (data, t);
455 }
456
457 void
458 scm_dynwind_fluid (SCM fluid, SCM value)
459 {
460 SCM data = scm_cons (fluid, value);
461 scm_dynwind_rewind_handler_with_scm (swap_fluid, data, SCM_F_WIND_EXPLICITLY);
462 scm_dynwind_unwind_handler_with_scm (swap_fluid, data, SCM_F_WIND_EXPLICITLY);
463 }
464
465 SCM
466 scm_i_make_initial_dynamic_state ()
467 {
468 SCM fluids = scm_c_make_vector (allocated_fluids_len, SCM_BOOL_F);
469 return scm_cell (scm_tc7_dynamic_state, SCM_UNPACK (fluids));
470 }
471
472 SCM_DEFINE (scm_make_dynamic_state, "make-dynamic-state", 0, 1, 0,
473 (SCM parent),
474 "Return a copy of the dynamic state object @var{parent}\n"
475 "or of the current dynamic state when @var{parent} is omitted.")
476 #define FUNC_NAME s_scm_make_dynamic_state
477 {
478 SCM fluids;
479
480 if (SCM_UNBNDP (parent))
481 parent = scm_current_dynamic_state ();
482
483 SCM_ASSERT (IS_DYNAMIC_STATE (parent), parent, SCM_ARG1, FUNC_NAME);
484 fluids = scm_vector_copy (DYNAMIC_STATE_FLUIDS (parent));
485 return scm_cell (scm_tc7_dynamic_state, SCM_UNPACK (fluids));
486 }
487 #undef FUNC_NAME
488
489 SCM_DEFINE (scm_dynamic_state_p, "dynamic-state?", 1, 0, 0,
490 (SCM obj),
491 "Return @code{#t} if @var{obj} is a dynamic state object;\n"
492 "return @code{#f} otherwise")
493 #define FUNC_NAME s_scm_dynamic_state_p
494 {
495 return scm_from_bool (IS_DYNAMIC_STATE (obj));
496 }
497 #undef FUNC_NAME
498
499 int
500 scm_is_dynamic_state (SCM obj)
501 {
502 return IS_DYNAMIC_STATE (obj);
503 }
504
505 SCM_DEFINE (scm_current_dynamic_state, "current-dynamic-state", 0, 0, 0,
506 (),
507 "Return the current dynamic state object.")
508 #define FUNC_NAME s_scm_current_dynamic_state
509 {
510 return SCM_I_CURRENT_THREAD->dynamic_state;
511 }
512 #undef FUNC_NAME
513
514 SCM_DEFINE (scm_set_current_dynamic_state, "set-current-dynamic-state", 1,0,0,
515 (SCM state),
516 "Set the current dynamic state object to @var{state}\n"
517 "and return the previous current dynamic state object.")
518 #define FUNC_NAME s_scm_set_current_dynamic_state
519 {
520 scm_i_thread *t = SCM_I_CURRENT_THREAD;
521 SCM old = t->dynamic_state;
522 SCM_ASSERT (IS_DYNAMIC_STATE (state), state, SCM_ARG1, FUNC_NAME);
523 t->dynamic_state = state;
524 return old;
525 }
526 #undef FUNC_NAME
527
528 static void
529 swap_dynamic_state (SCM loc)
530 {
531 SCM_SETCAR (loc, scm_set_current_dynamic_state (SCM_CAR (loc)));
532 }
533
534 void
535 scm_dynwind_current_dynamic_state (SCM state)
536 {
537 SCM loc = scm_cons (state, SCM_EOL);
538 SCM_ASSERT (IS_DYNAMIC_STATE (state), state, SCM_ARG1, NULL);
539 scm_dynwind_rewind_handler_with_scm (swap_dynamic_state, loc,
540 SCM_F_WIND_EXPLICITLY);
541 scm_dynwind_unwind_handler_with_scm (swap_dynamic_state, loc,
542 SCM_F_WIND_EXPLICITLY);
543 }
544
545 void *
546 scm_c_with_dynamic_state (SCM state, void *(*func)(void *), void *data)
547 {
548 void *result;
549 scm_dynwind_begin (SCM_F_DYNWIND_REWINDABLE);
550 scm_dynwind_current_dynamic_state (state);
551 result = func (data);
552 scm_dynwind_end ();
553 return result;
554 }
555
556 SCM_DEFINE (scm_with_dynamic_state, "with-dynamic-state", 2, 0, 0,
557 (SCM state, SCM proc),
558 "Call @var{proc} while @var{state} is the current dynamic\n"
559 "state object.")
560 #define FUNC_NAME s_scm_with_dynamic_state
561 {
562 SCM result;
563 scm_dynwind_begin (SCM_F_DYNWIND_REWINDABLE);
564 scm_dynwind_current_dynamic_state (state);
565 result = scm_call_0 (proc);
566 scm_dynwind_end ();
567 return result;
568 }
569 #undef FUNC_NAME
570
571
572 void
573 scm_init_fluids ()
574 {
575 #include "libguile/fluids.x"
576 }
577
578 /*
579 Local Variables:
580 c-file-style: "gnu"
581 End:
582 */