/* Copyright 2016 Google Inc.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License. */

#ifndef NSYNC_TESTING_CLOSURE_H_
#define NSYNC_TESTING_CLOSURE_H_

/* A run-once, self-freeing closure. */
typedef struct closure_s {
	void (*f0) (void *);
} closure;

/* Run the closure *cl, and free it. */
void closure_run (closure *cl);

/* Fork a new thread running the closure *cl, which will be freed when the
   thread exits.  */
void closure_fork (closure *cl);

/* To create a closure, declare a closure constructor with the right function arguments.

   For functions taking no arguments, use
	CLOSURE_DECL_BODY0 (foo)
   to generate the static routine:
	static closure *closure_foo (void (*f) (void));
   that will return a closure for any function *f that takes no argument.

   For an 1-argument function, use
	CLOSURE_DECL_BODY1 (foo, type)
   to generate the static routine:
	static closure *closure_foo (void (*f) (type), type x);
   that will return a closure for any function taking a single argument of the
   specified type.

   For an 2-argument function, use
	CLOSURE_DECL_BODY2 (foo, type0, type1)
   to generate the static routine:
	static closure *closure_foo (void (*f) (type0, type1), type0 x0, type1 x1);
   that will return a closure for any function taking a "type0" argument, and
   a "type1" argument.

   And so on, up to 9 arguments.

   For example, to make closures out of qsort():

	// First, just once (per module) define:
	//      static closure *closure_qsort_args (
	//              void (*f) (void *, size_t, size_t, int (*)(const void *, const void *))
	//              void *x0, size_t x1, size_t x2, int (*x3)(const void *, const void *));
	// by writing:
	CLOSURE_DECL_BODY4 (qsort_args, void *, size_t, size_t, int (*)(const void *, const void *))

	// Second, for each closure to be created, write something like this:
	closure *cl = closure_qsort_args (array, n_elements, sizeof (array[0]), &elem_cmp);

	// Then to run (and free) each closure:
	closure_run (cl);
	// This is like calling
	//      qsort (array, n_elements, sizeof (array[0]), &elem_cmp);
	//      free (cl);
 */




/* ------------------------------------------------------------------ */
/* Internal macro details follow. */
#define CLOSURE_S0(x,e) e
#define CLOSURE_S1(x,e) x##0
#define CLOSURE_S2(x,e) x##0, x##1
#define CLOSURE_S3(x,e) x##0, x##1, x##2
#define CLOSURE_S4(x,e) x##0, x##1, x##2, x##3
#define CLOSURE_S5(x,e) x##0, x##1, x##2, x##3, x##4
#define CLOSURE_S6(x,e) x##0, x##1, x##2, x##3, x##4, x##5
#define CLOSURE_S7(x,e) x##0, x##1, x##2, x##3, x##4, x##5, x##6
#define CLOSURE_S8(x,e) x##0, x##1, x##2, x##3, x##4, x##5, x##6, x##7
#define CLOSURE_S9(x,e) x##0, x##1, x##2, x##3, x##4, x##5, x##6, x##7, x##8

#define CLOSURE_P0(x,y,p,s,t)
#define CLOSURE_P1(x,y,p,s,t) p x##0 y##0 t
#define CLOSURE_P2(x,y,p,s,t) p x##0 y##0 s x##1 y##1 t
#define CLOSURE_P3(x,y,p,s,t) p x##0 y##0 s x##1 y##1 s x##2 y##2 t
#define CLOSURE_P4(x,y,p,s,t) p x##0 y##0 s x##1 y##1 s x##2 y##2 s x##3 y##3 t
#define CLOSURE_P5(x,y,p,s,t) p x##0 y##0 s x##1 y##1 s x##2 y##2 s x##3 y##3 s x##4 y##4 t
#define CLOSURE_P6(x,y,p,s,t) p x##0 y##0 s x##1 y##1 s x##2 y##2 s x##3 y##3 s x##4 y##4 s \
				x##5 y##5 t
#define CLOSURE_P7(x,y,p,s,t) p x##0 y##0 s x##1 y##1 s x##2 y##2 s x##3 y##3 s x##4 y##4 s \
				x##5 y##5 s x##6 y##6 t
#define CLOSURE_P8(x,y,p,s,t) p x##0 y##0 s x##1 y##1 s x##2 y##2 s x##3 y##3 s x##4 y##4 s \
				x##5 y##5 s x##6 y##6 s x##7 y##7 t
#define CLOSURE_P9(x,y,p,s,t) p x##0 y##0 s x##1 y##1 s x##2 y##2 s x##3 y##3 s x##4 y##4 s \
				x##5 y##5 s x##6 y##6 s x##7 y##7 s x##8 y##8 t

#define CLOSURE_COMMA_ ,
#define CLOSURE_SEMI_ ;
#define CLOSURE_BLANK_

#define CLOSURE_DECL_BODY_N_(name, n) \
	struct closure_s_##name { \
		void (*f0) (void *); /* must be first; matches closure. */ \
		void (*f) (CLOSURE_S##n (closure_t_##name##_,void)); \
		CLOSURE_P##n (closure_t_##name##_, a, CLOSURE_BLANK_, \
			      CLOSURE_SEMI_, CLOSURE_SEMI_) \
	}; \
	static void closure_f0_##name (void *v) { \
		struct closure_s_##name *a = (struct closure_s_##name *) v; \
		(*a->f) (CLOSURE_S##n (a->a,CLOSURE_BLANK_)); \
		free (a); \
	} \
	static closure *closure_##name (void (*f) (CLOSURE_S##n (closure_t_##name##_,void)) \
					CLOSURE_P##n (closure_t_##name##_, a, CLOSURE_COMMA_, \
					CLOSURE_COMMA_, CLOSURE_BLANK_)) { \
		struct closure_s_##name *cl = (struct closure_s_##name *) malloc (sizeof (*cl)); \
		cl->f0 = &closure_f0_##name; \
		cl->f = f; \
		CLOSURE_P##n (cl->a, = a, CLOSURE_BLANK_, CLOSURE_SEMI_, CLOSURE_SEMI_) \
		return ((closure *) cl); \
	}


#define CLOSURE_DECL_BODY0(name) \
	CLOSURE_DECL_BODY_N_ (name, 0)
#define CLOSURE_DECL_BODY1(name, t0) \
	typedef t0 closure_t_##name##_0; \
	CLOSURE_DECL_BODY_N_ (name, 1)
#define CLOSURE_DECL_BODY2(name, t0, t1) \
	typedef t0 closure_t_##name##_0; \
	typedef t1 closure_t_##name##_1; \
	CLOSURE_DECL_BODY_N_ (name, 2)
#define CLOSURE_DECL_BODY3(name, t0, t1, t2) \
	typedef t0 closure_t_##name##_0; \
	typedef t1 closure_t_##name##_1; \
	typedef t2 closure_t_##name##_2; \
	CLOSURE_DECL_BODY_N_ (name, 3)
#define CLOSURE_DECL_BODY4(name, t0, t1, t2, t3) \
	typedef t0 closure_t_##name##_0; \
	typedef t1 closure_t_##name##_1; \
	typedef t2 closure_t_##name##_2; \
	typedef t3 closure_t_##name##_3; \
	CLOSURE_DECL_BODY_N_ (name, 4)
#define CLOSURE_DECL_BODY5(name, t0, t1, t2, t3, t4) \
	typedef t0 closure_t_##name##_0; \
	typedef t1 closure_t_##name##_1; \
	typedef t2 closure_t_##name##_2; \
	typedef t3 closure_t_##name##_3; \
	typedef t4 closure_t_##name##_4; \
	CLOSURE_DECL_BODY_N_ (name, 5)
#define CLOSURE_DECL_BODY6(name, t0, t1, t2, t3, t4, t5) \
	typedef t0 closure_t_##name##_0; \
	typedef t1 closure_t_##name##_1; \
	typedef t2 closure_t_##name##_2; \
	typedef t3 closure_t_##name##_3; \
	typedef t4 closure_t_##name##_4; \
	typedef t5 closure_t_##name##_5; \
	CLOSURE_DECL_BODY_N_ (name, 6)
#define CLOSURE_DECL_BODY7(name, t0, t1, t2, t3, t4, t5, t6) \
	typedef t0 closure_t_##name##_0; \
	typedef t1 closure_t_##name##_1; \
	typedef t2 closure_t_##name##_2; \
	typedef t3 closure_t_##name##_3; \
	typedef t4 closure_t_##name##_4; \
	typedef t5 closure_t_##name##_5; \
	typedef t6 closure_t_##name##_6; \
	CLOSURE_DECL_BODY_N_ (name, 7)
#define CLOSURE_DECL_BODY8(name, t0, t1, t2, t3, t4, t5, t6, t7) \
	typedef t0 closure_t_##name##_0; \
	typedef t1 closure_t_##name##_1; \
	typedef t2 closure_t_##name##_2; \
	typedef t3 closure_t_##name##_3; \
	typedef t4 closure_t_##name##_4; \
	typedef t5 closure_t_##name##_5; \
	typedef t6 closure_t_##name##_6; \
	typedef t7 closure_t_##name##_7; \
	CLOSURE_DECL_BODY_N_ (name, 8)
#define CLOSURE_DECL_BODY9(name, t0, t1, t2, t3, t4, t5, t6, t7, t8) \
	typedef t0 closure_t_##name##_0; \
	typedef t1 closure_t_##name##_1; \
	typedef t2 closure_t_##name##_2; \
	typedef t3 closure_t_##name##_3; \
	typedef t4 closure_t_##name##_4; \
	typedef t5 closure_t_##name##_5; \
	typedef t6 closure_t_##name##_6; \
	typedef t7 closure_t_##name##_7; \
	typedef t8 closure_t_##name##_8; \
	CLOSURE_DECL_BODY_N_ (name, 9)

#endif /*NSYNC_TESTING_CLOSURE_H_*/
