/*******************************************************************************
 * Copyright (c) 2007, 2016 Wind River Systems, Inc. and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 * The Eclipse Public License is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 * You may elect to redistribute this code under either of these licenses.
 *
 * Contributors:
 *     Wind River Systems - initial API and implementation
 *******************************************************************************/

/*
 * This module handles debug contexts and their state machine.
 */

#ifndef D_context
#define D_context

#include <tcf/config.h>
#include <tcf/framework/channel.h>
#include <tcf/framework/cpudefs.h>
#include <tcf/framework/errors.h>
#include <tcf/framework/sigsets.h>
#include <tcf/framework/link.h>
#include <tcf/framework/context-ext.h>

extern LINK context_root;

#define ctxl2ctxp(A)    ((Context *)((char *)(A) - offsetof(Context, ctxl)))
#define cldl2ctxp(A)    ((Context *)((char *)(A) - offsetof(Context, cldl)))

typedef void ContextAttachCallBack(int, Context *, void *);

/*
 * A context corresponds to an execution thread, process, address space, etc.
 * A context can belong to a parent context. Contexts hierarchy can be simple
 * plain list or it can form a tree. It is up to target agent developers to choose
 * layout that is most descriptive for a given target.
 *
 * Role of a context is defined by its capabilities. Clients learn the context
 * capabilities using functions like context_has_state(), context_can_resume(),
 * context_get_group(), etc. For example, if a context has a name and no other capabilities,
 * its role is to provide human readable label for a group of contexts - its children.
 */
struct Context {
    char                id[256];            /* context ID */
    char *              name;               /* human readable context name */
    LINK                cldl;               /* link that used to form a list of context children */
    LINK                ctxl;               /* link that used to form a list of all contexts */
    LINK                children;           /* context children double linked list */
    Context *           parent;             /* context parent */
    Context *           creator;            /* context creator */
    Context *           mem;                /* context memory space */
    int                 big_endian;         /* 0 - little endian, 1 -  big endian */
    unsigned int        mem_access;         /* bit set of memory access types represented by this context */
    unsigned int        reg_access;         /* bit set of register access types represented by this context */
    unsigned int        ref_count;          /* reference count, see context_lock() and context_unlock() */
    int                 stopped;            /* OS kernel has stopped this context */
    int                 stopped_by_bp;      /* stopped by breakpoint instruction */
    ContextBreakpoint** stopped_by_cb;      /* stopped by ContextBreakpoint - NULL terminated list of triggered ContextBreakpoint's */
    int                 stopped_by_exception;/* stopped by runtime exception (like SIGSEGV, etc.) */
    int                 stopped_by_funccall;/* stopped by return from injected function call */
    char *              exception_description;/* description of exception if stopped by runtime exception */
    int                 advanced;           /* when context is stopped, set to 1 if software execution has progressed */
    int                 exiting;            /* context is about to exit */
    int                 exited;             /* context exited */
    int                 event_notification; /* set to 1 when calling one of ContextEventListener call-backs for this context */
    int                 pending_intercept;  /* host is waiting for this context to be suspended */
    SigSet              pending_signals;    /* bit set of signals that were received, but not handled yet */
    SigSet              sig_dont_stop;      /* bit set of signals that should not be intercepted by the debugger */
    SigSet              sig_dont_pass;      /* bit set of signals that should not be delivered to the context */
    int                 signal;             /* signal that stopped this context */
};

/*
 * Debug context suspend reason.
 */
extern const char * REASON_USER_REQUEST;
extern const char * REASON_STEP;
extern const char * REASON_ACTIVE;
extern const char * REASON_BREAKPOINT;
extern const char * REASON_EXCEPTION;
extern const char * REASON_CONTAINER;
extern const char * REASON_WATCHPOINT;
extern const char * REASON_SIGNAL;
extern const char * REASON_SHAREDLIB;
extern const char * REASON_ERROR;

/*
 * Values of "mem_access".
 * Target system can support multiple different memory access types, like instruction and data access.
 * Different access types can use different logic for address translation and memory mapping, so they can
 * end up accessing different data bits, even if address is the same.
 * Each distinct access type should be represented by separate memory context.
 * A memory context can represent multiple access types if they are equivalent - all access same memory bits.
 * Same data bits can be exposed through multiple memory contexts.
 */
#define MEM_ACCESS_INSTRUCTION  0x0001      /* Context represent instructions fetch access */
#define MEM_ACCESS_DATA         0x0002      /* Context represents data access */
#define MEM_ACCESS_IO           0x0004      /* Context represents IO peripherals */
#define MEM_ACCESS_USER         0x0008      /* Context represents a user (e.g. application running in Linux) view to memory */
#define MEM_ACCESS_SUPERVISOR   0x0010      /* Context represents a supervisor (e.g. Linux kernel) view to memory */
#define MEM_ACCESS_HYPERVISOR   0x0020      /* Context represents a hypervisor view to memory */
#define MEM_ACCESS_VIRTUAL      0x0040      /* Context uses virtual addresses */
#define MEM_ACCESS_PHYSICAL     0x0080      /* Context uses physical addresses */
#define MEM_ACCESS_CACHE        0x0100      /* Context is a cache */
#define MEM_ACCESS_TLB          0x0200      /* Context is a TLB memory */

/*
 * Values of "reg_access".
 */
#define REG_ACCESS_RD_RUNNING   0x0001      /* Context supports reading registers while running */
#define REG_ACCESS_WR_RUNNING   0x0002      /* Context supports writing registers while running */
#define REG_ACCESS_RD_STOP      0x0004      /* Debugger should stop the context to read registers */
#define REG_ACCESS_WR_STOP      0x0008      /* Debugger should stop the context to write registers */

/*
 * MemoryErrorInfo is used to retrieve additional information about memory access error.
 */
typedef struct MemoryErrorInfo {
    int error;                      /* The memory access error code */
    size_t size_valid;              /* The number of bytes transferred successfully */
    size_t size_error;              /* The number of bytes that caused the error, starting at 'size_valid' offset */
} MemoryErrorInfo;

/* Memory map data types */
typedef struct MemoryMap MemoryMap;
typedef struct MemoryRegion MemoryRegion;
typedef struct MemoryRegionAttribute MemoryRegionAttribute;

struct MemoryMap {
    unsigned region_cnt;
    unsigned region_max;
    MemoryRegion * regions;
};

struct MemoryRegion {
    ContextAddress addr;            /* Region address in context memory */
    ContextAddress size;            /* Region size */
    uint64_t file_offs;             /* File offset of the region */
    uint64_t file_size;             /* File size of the region */
    int bss;                        /* 1 if the region is BSS segment */
    dev_t dev;                      /* Region file device ID */
    ino_t ino;                      /* Region file inode */
    char * file_name;               /* Region file name */
    char * sect_name;               /* Region file section name, can be NULL */
    unsigned flags;                 /* Region flags, see MM_FLAG* */
    unsigned valid;                 /* Region valid flags, see MM_VALID* */
    char * query;                   /* If not NULL, the region is only part of the memory map for contexts matches the query */
    char * id;                      /* Region ID, not NULL only if the region info is submitted by a client */
    MemoryRegionAttribute * attrs;  /* Additional memory region attributes */
    Channel * channel;              /* Not NULL if the region info is submitted by a client */
};

struct MemoryRegionAttribute {
    MemoryRegionAttribute * next;
    char * name;
    char * value;
};

#define MM_FLAG_R   1
#define MM_FLAG_W   2
#define MM_FLAG_X   4

/* These flags are used to resolve ambiguity between MemoryRegion field not set or set to 0.
 * When the field is not 0, it is valid regardless of the flag. */
#define MM_VALID_ADDR       1
#define MM_VALID_SIZE       2
#define MM_VALID_FILE_OFFS  4
#define MM_VALID_FILE_SIZE  8

/* Context resume modes */
#define RM_RESUME                   0 /* Resume normal execution of the context */
#define RM_STEP_OVER                1 /* Step over a single instruction */
#define RM_STEP_INTO                2 /* Step a single instruction */
#define RM_STEP_OVER_LINE           3 /* Step over a single source code line */
#define RM_STEP_INTO_LINE           4 /* Step a single source code line */
#define RM_STEP_OUT                 5 /* Run until control returns from current function */
#define RM_REVERSE_RESUME           6 /* Start running backwards */
#define RM_REVERSE_STEP_OVER        7 /* Reverse of RM_STEP_OVER - run backwards over a single instruction */
#define RM_REVERSE_STEP_INTO        8 /* Reverse of RM_STEP_INTO: "un-execute" the previous instruction */
#define RM_REVERSE_STEP_OVER_LINE   9 /* Reverse of RM_STEP_OVER_LINE */
#define RM_REVERSE_STEP_INTO_LINE  10 /* Reverse of RM_STEP_INTO_LINE */
#define RM_REVERSE_STEP_OUT        11 /* Reverse of RM_STEP_OUT */
#define RM_STEP_OVER_RANGE         12 /* Step over instructions until PC is outside the specified range */
#define RM_STEP_INTO_RANGE         13 /* Step instruction until PC is outside the specified range for any reason */
#define RM_REVERSE_STEP_OVER_RANGE 14 /* Reverse of RM_STEP_OVER_RANGE */
#define RM_REVERSE_STEP_INTO_RANGE 15 /* Reverse of RM_STEP_INTO_RANGE */
#define RM_UNTIL_ACTIVE            16 /* Run until the context becomes active - scheduled to run on a target CPU */
#define RM_REVERSE_UNTIL_ACTIVE    17 /* Run reverse until the context becomes active */
/* These modes are used internally by the agent and should not be exposed to remote clients */
#define RM_DETACH                  18 /* Detach the context */
#define RM_TERMINATE               19 /* Terminate the context */
#define RM_SKIP_PROLOGUE           20 /* Skip function prologue */
#define RM_UNDEF                   21

/* Mode flags for context_attach() */
#define CONTEXT_ATTACH_SELF      0x01 /* The process is forked child - it will attach itself */
#define CONTEXT_ATTACH_CHILDREN  0x02 /* Enable auto-attaching of children of the process */
#define CONTEXT_ATTACH_NO_STOP   0x04 /* Don't stop after attach */
#define CONTEXT_ATTACH_NO_MAIN   0x08 /* Don't stop at main() */

/*
 * Convert PID to TCF Context ID.
 * Note: PID to ID mapping is supported for native debugging only.
 */
extern char * pid2id(pid_t pid, pid_t parent);

/*
 * Convert TCF Context ID to PID.
 * Note: PID to ID mapping is supported for native debugging only.
 */
extern pid_t id2pid(const char * id, pid_t * parent);

/*
 * Search Context record by TCF Context ID.
 */
extern Context * id2ctx(const char * id);

/*
 * Register an extension of struct Context.
 * Return offset of extension data area.
 * Additional memory of given size will be allocated in each context struct.
 * Client are allowed to call this function only during initialization.
 */
extern size_t context_extension(size_t size);

/*
 * Create a Context object.
 * The function is called by context implementation.
 * It is not supposed to be called by clients.
 */
extern Context * create_context(const char * id);

/*
 * Clear a memory map - dispose all entries.
 */
extern void context_clear_memory_map(MemoryMap * map);

#if ENABLE_DebugContext

/*
 * Get context full name that includes ancestor names.
 */
extern const char * context_full_name(Context * ctx);

/*
 * Get human redable name of current state of a context.
 */
extern const char * context_state_name(Context * ctx);

/*
 * Get state change reason of a context.
 * Reason can be any text, but if it is one of predefined strings,
 * a generic client might be able to handle it better.
 * See REASON_* for predefined reason names.
 */
extern const char * context_suspend_reason(Context * ctx);

/*
 * Find a context by PID
 * Both process and main thread can have same PID.
 * 'thread' = 0: search for process, otherwise search for a thread.
 * Note: PID to context mapping is supported for native debugging only.
 */
extern Context * context_find_from_pid(pid_t pid, int thread);

/*
 * Trigger self attachment e.g. of forked child
 * Only available on Linux/Unix.
 */
extern int context_attach_self(void);

/*
 * Start tracing of a process.
 * Client provides a call-back function that will be called when context is attached.
 * The callback function args are error code, the context and client data.
 * 'mode' - attach mode flags, see CONTEXT_ATTACH_*.
 * Note: attaching by PID is supported for native debugging only.
 */
extern int context_attach(pid_t pid, ContextAttachCallBack * done, void * client_data, int mode);

/*
 * Increment reference counter of Context object.
 * While ref count > 0 object will not be deleted even when context exits.
 */
extern void context_lock(Context * ctx);

/*
 * Decrement reference counter.
 * If ref count == 0, delete Context object.
 */
extern void context_unlock(Context * ctx);

/*
 * Return 1 if the context has running/stopped state, return 0 othewise
 */
extern int context_has_state(Context * ctx);

/*
 * Get context memory properties.
 * 'values' are JSON objects.
 * Return -1 and set errno if cannot access the properties.
 * Return 0 on success.
 */
#if ENABLE_ContextMemoryProperties
extern int context_get_memory_properties(Context * ctx, const char *** names, const char *** values, int * cnt);
#endif

/*
 * Get additional context properties.
 * 'values' are JSON objects.
 * Return -1 and set errno if cannot access the properties.
 * Return 0 on success.
 */
#if ENABLE_ContextExtraProperties
extern int context_get_extra_properties(Context * ctx, const char *** names, const char *** values, int * cnt);
#endif

/*
 * Get additional context state properties.
 * 'values' are JSON objects.
 * Return -1 and set errno if cannot access the properties.
 * Return 0 on success.
 */
#if ENABLE_ContextStateProperties
extern int context_get_state_properties(Context * ctx, const char *** names, const char *** values, int * cnt);
#endif

/*
 * Stop execution of the context.
 * Execution can be resumed by calling context_resume()
 * Return -1 and set errno if the context cannot be stopped.
 * Return 0 on success.
 */
extern int context_stop(Context * ctx);

/*
 * Resume execution of the context using give execution mode.
 * See RM_* for mode definitions.
 * Return -1 and set errno if the context cannot be resumed.
 * Return 0 on success.
 */
extern int context_resume(Context * ctx, int mode, ContextAddress range_start, ContextAddress range_end);

/*
 * Check if given resume mode is supported.
 * See RM_* for mode definitions.
 * Return 0 if not supported, 1 if supported.
 */
extern int context_can_resume(Context * ctx, int mode);

/*
 * Resume normal execution of the context.
 * Return -1 and set errno if the context cannot be resumed.
 * Return 0 on success.
 * Deprecated: use context_resume(ctx, RM_RESUME, 0, 0).
 */
extern int context_continue(Context * ctx);

/*
 * Perform single instruction step on the context.
 * Return -1 and set errno if the context cannot be single stepped.
 * Return 0 on success.
 * Deprecated: use context_resume(ctx, RM_STEP_INTO, 0, 0).
 */
extern int context_single_step(Context * ctx);

/*
 * Retrieve context memory map.
 * Return -1 and set errno if the map cannot be retrieved.
 * Return 0 on success.
 * Note: the caller owns MemoryMap object and all its contents, it can call loc_free() on it at any time.
 * When context_get_memory_map() is called, 'map' is empty: region_cnt == 0,
 * but 'map->regions' can be pre-allocated: 'map->regions' can be not NULL and 'map->region_max' > 0.
 * The function adds memory region descriptions into it, doing loc_strdup() for things like file names.
 * The function implementation should not retain references to 'map' or its contents.
 */
extern int context_get_memory_map(Context * ctx, MemoryMap * map);

/*
 * Write context memory.
 * Implementation calls check_breakpoints_on_memory_write() before writing to context memory,
 * which can change contents of the buffer.
 * Return -1 and set errno if the context memory cannot be written.
 * Return 0 on success.
 */
extern int context_write_mem(Context * ctx, ContextAddress address, void * buf, size_t size);

/*
 * Read context memory.
 * Implementation calls check_breakpoints_on_memory_read() after reading context memory.
 * Return -1 and set errno if the context memory cannot be read.
 * Return 0 on success.
 */
extern int context_read_mem(Context * ctx, ContextAddress address, void * buf, size_t size);

/*
 * Retrieve addition information about error reported by last memory access.
 * Return -1 and set errno if the info cannot be read.
 * Return 0 on success.
 */
#if ENABLE_ExtendedMemoryErrorReports
extern int context_get_mem_error_info(MemoryErrorInfo * info);
#endif

typedef struct MemoryAccessMode {
    unsigned word_size; /* 0 means any */
    int continue_on_error;
    int bypass_addr_check;
    int bypass_cache_sync;
    int verify;
} MemoryAccessMode;

/* Optional memory access function that take additional argument: access mode. */
#if ENABLE_MemoryAccessModes

/*
 * Write context memory.
 * Implementation calls check_breakpoints_on_memory_write() before writing to context memory,
 * which can change contents of the buffer.
 * Return -1 and set errno if the context memory cannot be written.
 * Return 0 on success.
 */
extern int context_write_mem_ext(Context * ctx, MemoryAccessMode * mode, ContextAddress address, void * buf, size_t size);

/*
 * Read context memory.
 * Implementation calls check_breakpoints_on_memory_read() after reading context memory.
 * Return -1 and set errno if the context memory cannot be read.
 * Return 0 on success.
 */
extern int context_read_mem_ext(Context * ctx, MemoryAccessMode * mode, ContextAddress address, void * buf, size_t size);

#endif

/*
 * Write 'size' bytes into context register starting at offset 'offs'.
 * Return -1 and set errno if the register cannot be written.
 * Return 0 on success.
 */
extern int context_write_reg(Context * ctx, RegisterDefinition * def, unsigned offs, unsigned size, void * buf);

/*
 * Read 'size' bytes from context register starting at offset 'offs'.
 * Return -1 and set errno if the register cannot be read.
 * Return 0 on success.
 */
extern int context_read_reg(Context * ctx, RegisterDefinition * def, unsigned offs, unsigned size, void * buf);

/*
 * Return context word size in bytes.
 * It is the default value of sizeof(void*) when debug symbols are not available.
 */
extern unsigned context_word_size(Context * ctx);

/*
 * Map an address in given address space to a unique canonical memory location.
 * A target system can have same block (page) of memory mapped to different address spaces at different addresses.
 * This function should return memory space and address that uniquely identify the location.
 * 'block_addr' and 'block_size' are optional arguments that clients can use to retrieve
 * the memory block start address and size. Clients can use this information to map a range of addresses
 * with a single function call.
 * Return -1 and set errno if canonical address cannot be resolved.
 * Return 0 on success.
 */
extern int context_get_canonical_addr(Context * ctx, ContextAddress addr,
        Context ** canonical_ctx, ContextAddress * canonical_addr,
        ContextAddress * block_addr, ContextAddress * block_size);

/*
 * Get a context that represents a group of related contexts.
 * Implementation can choose a member of a group to represent the group,
 * or it can create a separate context object for that.
 * Clients can use this function to check if two or more contexts belong to same group,
 * for example:
 *     if (context_get_group(ctx1, group) == context_get_group(ctx2, group) ...
 *
 * See CONTEXT_GROUP_* for possible values of 'group' argument.
 */
extern Context * context_get_group(Context * ctx, int group);

/*
 * "stop" context group - all contexts that need to be stopped to make sure
 * that memory contents in a particular address space is stable.
 * Note: NULL is not allowed as the group handle.
 * If the grouping is not applicable to a context,
 * context_get_group() should return the context itself.
 */
#define CONTEXT_GROUP_STOP          1

/*
 * "breakpoint" context group - all contexts for which evaluation of breakpoint
 * location should produce same list of addresses.
 * context_get_group(ctx, CONTEXT_GROUP_BREAKPOINT) == NULL means
 * the context does not support breakpoints.
 */
#define CONTEXT_GROUP_BREAKPOINT    2

/*
 * "intercept" context group - all contexts which should be intercepted
 * when any member of the group is intercepted for any reason.
 * Note: NULL is not allowed as the group handle.
 * If the grouping is not applicable to a context,
 * context_get_group() should return the context itself.
 */
#define CONTEXT_GROUP_INTERCEPT     3

/*
 * "process" context group - all contexts that share same memory address space,
 * memory map and executable files.
 * Note: NULL is not allowed as the group handle.
 * If the grouping is not applicable to a context,
 * context_get_group() should return the context itself.
 */
#define CONTEXT_GROUP_PROCESS       4

/*
 * "CPU" context group - all contexts that belong to same CPU.
 * On SMP systems, all CPUs (cores) of same type should be members of same group.
 * Note: NULL is not allowed as the group handle.
 * If the grouping is not applicable to a context,
 * context_get_group() should return the context itself.
 */
#define CONTEXT_GROUP_CPU           5

/*
 * "Symbols" context group - all contexts that share same symbol reader data,
 * including symbol files, source file paths and user defined memory map entries.
 * Note: NULL is not allowed as the group handle.
 * If the grouping is not applicable to a context,
 * context_get_group() should return the context itself.
 */
#define CONTEXT_GROUP_SYMBOLS       6

/*
 * Debug context implementation can support low-level breakpoints.
 * The support is optional, if not implemented, the agent will use generic code
 * to plant software breakpoints. Hardware breakpoints is an example of breakpoints
 * that can be implemented by debug context.
 *
 * ContextBreakpoint struct is used by the agent to communicate breakpoint properties to
 * debug context implementation. Generic code in the Breakpoints service handles common
 * breakpoint properties, like source code position and location expression. Debug context
 * implementation can support additional breakpoint properties. The implementation can
 * get values of the properties using Breakpoint service API: iterate_context_breakpoint_links()
 * and get_breakpoint_attributes().
 */
struct ContextBreakpoint {
    Context * ctx;              /* breakpoint context, one of returned by
                                 * context_get_group(..., CONTEXT_GROUP_BREAKPOINT) */
    ContextAddress address;     /* breakpoint address, or 0 if CTX_BP_ACCESS_NO_ADDRESS */
    ContextAddress length;      /* length of the breakpoint address range */
    unsigned access_types;      /* memory access type, bit set of CTX_BP_ACCESS_* */
    unsigned id;                /* to be used by debug context implementation */
    void * ext;                 /* to be used by debug context implementation */
};

#define CTX_BP_ACCESS_DATA_READ      0x01
#define CTX_BP_ACCESS_DATA_WRITE     0x02
#define CTX_BP_ACCESS_INSTRUCTION    0x04
#define CTX_BP_ACCESS_CHANGE         0x08
#define CTX_BP_ACCESS_VIRTUAL        0x10
#define CTX_BP_ACCESS_SOFTWARE       0x20
#define CTX_BP_ACCESS_NO_ADDRESS     0x40

/*
 * Return bitmask of supported CTX_BP_ACCESS_* values.
 */
extern int context_get_supported_bp_access_types(Context * ctx);

/*
 * Plant a breakpoint.
 * Return 0 on success, or return -1 and set errno on error.
 * If error code is ERR_UNSUPPORTED, Breakpoints service will
 * try to plant the breakpoint as a generic software breakpoint.
 */
extern int context_plant_breakpoint(ContextBreakpoint * bp);

/*
 * Un-plant (remove) a breakpoint.
 * Return 0 on success, or return -1 and set errno on error.
 * 'bp' must be a breakpoint that was planted by context_plant_breakpoint().
 */
extern int context_unplant_breakpoint(ContextBreakpoint * bp);

/*
 * Get context breakpoint capabilities.
 * 'values' are JSON objects.
 * 'ctx' can be NULL, it means a client has requested global capabilities.
 * Return -1 and set errno if cannot access the capabilities.
 * Return 0 on success.
 */
#if ENABLE_ContextBreakpointCapabilities
extern int context_get_breakpoint_capabilities(Context * ctx, const char *** names, const char *** values, int * cnt);
#endif

/*
 * Get extended breakpoint status properties.
 * 'values' are JSON objects.
 * Return -1 and set errno if cannot access the status.
 * Return 0 on success.
 */
#if ENABLE_ExtendedBreakpointStatus
extern int context_get_breakpoint_status(ContextBreakpoint * bp, const char *** names, const char *** values, int * cnt);
#endif

#if ENABLE_ContextISA
typedef struct {
    ContextAddress addr;
    ContextAddress size;
    ContextAddress alignment;
    ContextAddress max_instruction_size;
    const char * isa;
    const char * def;
    uint8_t * bp_encoding;  /* Encoding of breakpoint instruction */
    size_t bp_size;         /* Size of breakpoint instruction */
} ContextISA;

/*
 * Get context Instruction Set Architecture information.
 * Return -1 and set errno if cannot access the status.
 * Return 0 on success.
 */
extern int context_get_isa(Context * ctx, ContextAddress addr, ContextISA * isa);
#endif

/*
 * Functions that notify listeners of various context event.
 * These function are called by context implementation.
 * They are not supposed to be called by clients.
 */
extern void send_context_created_event(Context * ctx);
extern void send_context_changed_event(Context * ctx);
extern void send_context_stopped_event(Context * ctx);
extern void send_context_started_event(Context * ctx);
extern void send_context_exited_event(Context * ctx);

#if ENABLE_ContextIdHashTable
/*
 * Add context to ID hash table, so it can be found by id2ctx().
 * It is helper function to create "hidden" contexts.
 */
extern void add_context_to_id_hash_table(Context * ctx);
#endif

extern void ini_contexts(void);
extern void init_contexts_sys_dep(void);

#endif /* ENABLE_DebugContext */

typedef struct ContextEventListener {
    void (*context_created)(Context * ctx, void * client_data);
    void (*context_exited )(Context * ctx, void * client_data);
    void (*context_stopped)(Context * ctx, void * client_data);
    void (*context_started)(Context * ctx, void * client_data);
    void (*context_changed)(Context * ctx, void * client_data);
    void (*context_disposed)(Context * ctx, void * client_data);
} ContextEventListener;

extern void add_context_event_listener(ContextEventListener * listener, void * client_data);

#endif
