ifndef RMAGICK_GVL_HPP define RMAGICK_GVL_HPP

extern void rm_gvl_init(void); extern void rm_gvl_check_readable(const void *); extern void rm_gvl_check_writable(const void *); extern bool rm_gvl_in_use(const void *);

/**

* A call to an ImageMagick function without the GVL, which a Fiber scheduler
* with a worker pool may run on a worker thread. Register the Ruby objects the
* call uses and what the caller releases after it, then call run().
*/

class rm_gvl_call { public:

rm_gvl_call(gvl_function_t *fp, void *args);

rm_gvl_call &read(VALUE obj);
rm_gvl_call &read(const void *ptr);
rm_gvl_call &update(VALUE obj);
rm_gvl_call &update(const void *ptr);
rm_gvl_call &read_each(VALUE ary);
rm_gvl_call &update_each(VALUE ary);
rm_gvl_call &release(ExceptionInfo *exception);
rm_gvl_call &release(ImageInfo *info);
rm_gvl_call &release(DrawInfo *draw_info);
rm_gvl_call &release(KernelInfo *kernel);
rm_gvl_call &destroy(Image *image);
rm_gvl_call &split(Image *images);

if defined(IMAGEMAGICK_7)

rm_gvl_call &restore_mask(Image *image, ChannelType channel_mask);

endif

rm_gvl_call &free_buffer(void *buffer);
rm_gvl_call &relinquish(void *memory);
rm_gvl_call &free_result();
rm_gvl_call &keep_thread(bool keep = true);
rm_gvl_call &cleanup(void (*release)(void *, intptr_t), void *ptr, intptr_t arg = 0);
rm_gvl_call &restore(void (*release)(void *, intptr_t), void *ptr, intptr_t arg = 0, size_t size = 0);

template <typename T> T run();

private:

enum ResultType { ResultIgnored, ResultImage, ResultMemory };
enum { MaxObjects = 4, MaxCleanups = 4 };

typedef struct
{
    VALUE obj;
    const void *ptr;
    bool update;
    bool each;
} object_t;

typedef struct
{
    void (*release)(void *, intptr_t);
    void *ptr;
    intptr_t arg;
    size_t size;
    bool restore;
} cleanup_t;

gvl_function_t *fp;
void *args;
object_t objects[MaxObjects];
int nobjects;
cleanup_t cleanups[MaxCleanups];
int ncleanups;
ResultType result_type;
bool keep;

rm_gvl_call &add_object(VALUE obj, const void *ptr, bool update, bool each);
rm_gvl_call &add_cleanup(void (*release)(void *, intptr_t), void *ptr, intptr_t arg, size_t size, bool restore);
void *call(ResultType type);
void *call_here(ResultType type);
void unwind(ResultType type, void *result, bool abandoned = false);

};

template <typename T> T rm_gvl_call::run() {

return (T)(uintptr_t)call(result_type);

}

template <> Image *rm_gvl_call::run<Image *>(); template <> void rm_gvl_call::run<void>();

//! declare the arguments of the GVL stub of name and an rm_gvl_call named var that calls it define DECLARE_GVL_CALL(var, name, …) \

GVL_STRUCT_TYPE(name) var##_args = { __VA_ARGS__ }; \
rm_gvl_call var(GVL_FUNC(name), &var##_args)

//! declare the arguments of type and an rm_gvl_call named var that calls fp with them define DECLARE_GVL_CALL_FP(var, type, fp, …) \

GVL_STRUCT_TYPE(type) var##_args = { __VA_ARGS__ }; \
rm_gvl_call var(fp, &var##_args)

endif