/*
 *	BIRD -- Deferring calls to the end of the task
 *
 *	(c) 2024       Maria Matejka <mq@jmq.cz>
 *
 *	Can be freely distributed and used under the terms of the GNU GPL.
 */

/**
 * There are cases when we need to just run something multiple times after the
 * current task ends, mostly for cleanup reasons, and it doesn't need the
 * full-blown event list overhead. Therefore, one just can use this tool
 * instead. */

#ifndef _BIRD_LIB_DEFER_H_
#define _BIRD_LIB_DEFER_H_

#include "lib/birdlib.h"
#include "lib/event.h"
#include "lib/resource.h"
#include "lib/io-loop.h"

#define DEFER_MAX_SIZE		384

struct deferred_call {
  struct deferred_call *next;
  void (*hook)(struct deferred_call *);
};

struct deferred {
  event e;
  linpool *lp;
  struct deferred_call *first, **last;
};

extern _Thread_local struct deferred local_deferred;
void defer_init(linpool *lp);

static inline struct deferred_call *
defer_call(struct deferred_call *call, size_t actual_size)
{
  /* Reallocate the call to the appropriate linpool */
  ASSERT_DIE(actual_size < DEFER_MAX_SIZE);
  struct deferred_call *a = lp_alloc(local_deferred.lp, actual_size);
  memcpy(a, call, actual_size);

  /* If first, send the actual event to the local thread */
  if (local_deferred.last == &local_deferred.first)
    ev_send_defer(&local_deferred.e);

  /* Add to list */
  a->next = NULL;
  *local_deferred.last = a;
  local_deferred.last = &a->next;

  return a;
}

#define DEFER_CALL(_s)	SKIP_BACK(typeof(_s), dc, defer_call(&(_s).dc, sizeof (_s)))

#endif
