/* * $Id$ * Copyright (c) 2016-2020 Chung, Hyung-Hwan. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WAfRRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _MIO_H_ #define _MIO_H_ #include #include #if defined(_WIN32) typedef mio_uintptr_t mio_syshnd_t; #define MIO_SYSHND_INVALID (~(mio_uintptr_t)0) #else typedef int mio_syshnd_t; #define MIO_SYSHND_INVALID (-1) #endif typedef struct mio_devaddr_t mio_devaddr_t; struct mio_devaddr_t { int len; void* ptr; }; #define MIO_ERRMSG_CAPA (2048) /* [NOTE] ensure that it is a power of 2 */ #define MIO_LOG_CAPA_ALIGN 512 /* ========================================================================= */ typedef struct mio_t mio_t; typedef struct mio_dev_t mio_dev_t; typedef struct mio_dev_mth_t mio_dev_mth_t; typedef struct mio_dev_evcb_t mio_dev_evcb_t; typedef struct mio_svc_t mio_svc_t; typedef struct mio_q_t mio_q_t; typedef struct mio_wq_t mio_wq_t; typedef struct mio_cwq_t mio_cwq_t; typedef mio_intptr_t mio_iolen_t; /* NOTE: this is a signed type */ struct mio_iovec_t { void* iov_ptr; mio_oow_t iov_len; }; typedef struct mio_iovec_t mio_iovec_t; enum mio_errnum_t { MIO_ENOERR, /**< no error */ MIO_EGENERIC, /**< generic error */ MIO_ENOIMPL, /**< not implemented */ MIO_ESYSERR, /**< system error */ MIO_EINTERN, /**< internal error */ MIO_ESYSMEM, /**< insufficient system memory */ MIO_EOOMEM, /**< insufficient object memory */ MIO_EINVAL, /**< invalid parameter or data */ MIO_ENOENT, /**< data not found */ MIO_EEXIST, /**< existing/duplicate data */ MIO_EBUSY, /**< system busy */ MIO_EACCES, /**< access denied */ MIO_EPERM, /**< operation not permitted */ MIO_ENOTDIR, /**< not directory */ MIO_EINTR, /**< interrupted */ MIO_EPIPE, /**< pipe error */ MIO_EAGAIN, /**< resource temporarily unavailable */ MIO_EBADHND, /**< bad system handle */ MIO_EBADRE, /**< bad request or response */ MIO_EMFILE, /**< too many open files */ MIO_ENFILE, /**< too many open files */ MIO_EIOERR, /**< I/O error */ MIO_EECERR, /**< encoding conversion error */ MIO_EECMORE, /**< insufficient data for encoding conversion */ MIO_EBUFFULL, /**< buffer full */ MIO_ECONRF, /**< connection refused */ MIO_ECONRS, /**< connection reset */ MIO_ENOCAPA, /**< no capability */ MIO_ETMOUT, /**< timed out */ MIO_ENORSP, /**< no response */ MIO_EDEVMAKE, /**< unable to make device */ MIO_EDEVERR, /**< device error */ MIO_EDEVHUP /**< device hang-up */ }; typedef enum mio_errnum_t mio_errnum_t; struct mio_errinf_t { mio_errnum_t num; mio_ooch_t msg[MIO_ERRMSG_CAPA]; }; typedef struct mio_errinf_t mio_errinf_t; enum mio_option_t { MIO_TRAIT, MIO_LOG_MASK, MIO_LOG_MAXCAPA }; typedef enum mio_option_t mio_option_t; enum mio_stopreq_t { MIO_STOPREQ_NONE = 0, MIO_STOPREQ_TERMINATION, MIO_STOPREQ_WATCHER_ERROR }; typedef enum mio_stopreq_t mio_stopreq_t; /* ========================================================================= */ #define MIO_TMRIDX_INVALID ((mio_tmridx_t)-1) typedef mio_oow_t mio_tmridx_t; typedef struct mio_tmrjob_t mio_tmrjob_t; typedef void (*mio_tmrjob_handler_t) ( mio_t* mio, const mio_ntime_t* now, mio_tmrjob_t* tmrjob ); struct mio_tmrjob_t { void* ctx; mio_ntime_t when; mio_tmrjob_handler_t handler; mio_tmridx_t* idxptr; /* pointer to the index holder */ }; /* ========================================================================= */ struct mio_dev_mth_t { /* ------------------------------------------------------------------ */ /* mandatory. called in mio_dev_make() */ int (*make) (mio_dev_t* dev, void* ctx); /* ------------------------------------------------------------------ */ /* mandatory. called in mio_dev_kill(). also called in mio_dev_make() upon * failure after make() success. * * when 'force' is 0, the return value of -1 causes the device to be a * zombie. the kill method is called periodically on a zombie device * until the method returns 0. * * when 'force' is 1, the called should not return -1. If it does, the * method is called once more only with the 'force' value of 2. * * when 'force' is 2, the device is destroyed regardless of the return value. */ int (*kill) (mio_dev_t* dev, int force); /* ------------------------------------------------------------------ */ mio_syshnd_t (*getsyshnd) (mio_dev_t* dev); /* mandatory. called in mio_dev_make() after successful make() */ /* ------------------------------------------------------------------ */ /* return -1 on failure, 0 if no data is availble, 1 otherwise. * when returning 1, *len must be sent to the length of data read. * if *len is set to 0, it's treated as EOF. */ int (*read) (mio_dev_t* dev, void* data, mio_iolen_t* len, mio_devaddr_t* srcaddr); /* ------------------------------------------------------------------ */ int (*write) (mio_dev_t* dev, const void* data, mio_iolen_t* len, const mio_devaddr_t* dstaddr); int (*writev) (mio_dev_t* dev, const mio_iovec_t* iov, mio_iolen_t* iovcnt, const mio_devaddr_t* dstaddr); /* ------------------------------------------------------------------ */ int (*ioctl) (mio_dev_t* dev, int cmd, void* arg); }; struct mio_dev_evcb_t { /* return -1 on failure. 0 or 1 on success. * when 0 is returned, it doesn't attempt to perform actual I/O. * when 1 is returned, it attempts to perform actual I/O. */ int (*ready) (mio_dev_t* dev, int events); /* return -1 on failure, 0 or 1 on success. * when 0 is returned, the main loop stops the attempt to read more data. * when 1 is returned, the main loop attempts to read more data without*/ int (*on_read) (mio_dev_t* dev, const void* data, mio_iolen_t len, const mio_devaddr_t* srcaddr); /* return -1 on failure, 0 on success. * wrlen is the length of data written. it is the length of the originally * posted writing request for a stream device. For a non stream device, it * may be shorter than the originally posted length. */ int (*on_write) (mio_dev_t* dev, mio_iolen_t wrlen, void* wrctx, const mio_devaddr_t* dstaddr); }; struct mio_q_t { mio_q_t* q_next; mio_q_t* q_prev; }; #define MIO_Q_INIT(q) ((q)->q_next = (q)->q_prev = (q)) #define MIO_Q_TAIL(q) ((q)->q_prev) #define MIO_Q_HEAD(q) ((q)->q_next) #define MIO_Q_IS_EMPTY(q) (MIO_Q_HEAD(q) == (q)) #define MIO_Q_IS_NODE(q,x) ((q) != (x)) #define MIO_Q_IS_HEAD(q,x) (MIO_Q_HEAD(q) == (x)) #define MIO_Q_IS_TAIL(q,x) (MIO_Q_TAIL(q) == (x)) #define MIO_Q_NEXT(x) ((x)->q_next) #define MIO_Q_PREV(x) ((x)->q_prev) #define MIO_Q_LINK(p,x,n) do { \ mio_q_t* __pp = (p), * __nn = (n); \ (x)->q_prev = (p); \ (x)->q_next = (n); \ __nn->q_prev = (x); \ __pp->q_next = (x); \ } while (0) #define MIO_Q_UNLINK(x) do { \ mio_q_t* __pp = (x)->q_prev, * __nn = (x)->q_next; \ __nn->q_prev = __pp; __pp->q_next = __nn; \ } while (0) #define MIO_Q_REPL(o,n) do { \ mio_q_t* __oo = (o), * __nn = (n); \ __nn->q_next = __oo->q_next; \ __nn->q_next->q_prev = __nn; \ __nn->q_prev = __oo->q_prev; \ __nn->q_prev->q_next = __nn; \ } while (0) /* insert an item at the back of the queue */ /*#define MIO_Q_ENQ(wq,x) MIO_Q_LINK(MIO_Q_TAIL(wq), x, MIO_Q_TAIL(wq)->next)*/ #define MIO_Q_ENQ(wq,x) MIO_Q_LINK(MIO_Q_TAIL(wq), x, wq) /* remove an item in the front from the queue */ #define MIO_Q_DEQ(wq) MIO_Q_UNLINK(MIO_Q_HEAD(wq)) /* completed write queue */ struct mio_cwq_t { mio_cwq_t* q_next; mio_cwq_t* q_prev; mio_iolen_t olen; void* ctx; mio_dev_t* dev; mio_devaddr_t dstaddr; }; #define MIO_CWQ_INIT(cwq) ((cwq)->q_next = (cwq)->q_prev = (cwq)) #define MIO_CWQ_TAIL(cwq) ((cwq)->q_prev) #define MIO_CWQ_HEAD(cwq) ((cwq)->q_next) #define MIO_CWQ_IS_EMPTY(cwq) (MIO_CWQ_HEAD(cwq) == (cwq)) #define MIO_CWQ_IS_NODE(cwq,x) ((cwq) != (x)) #define MIO_CWQ_IS_HEAD(cwq,x) (MIO_CWQ_HEAD(cwq) == (x)) #define MIO_CWQ_IS_TAIL(cwq,x) (MIO_CWQ_TAIL(cwq) == (x)) #define MIO_CWQ_NEXT(x) ((x)->q_next) #define MIO_CWQ_PREV(x) ((x)->q_prev) #define MIO_CWQ_LINK(p,x,n) MIO_Q_LINK((mio_q_t*)p,(mio_q_t*)x,(mio_q_t*)n) #define MIO_CWQ_UNLINK(x) MIO_Q_UNLINK((mio_q_t*)x) #define MIO_CWQ_REPL(o,n) MIO_Q_REPL(o,n); #define MIO_CWQ_ENQ(cwq,x) MIO_CWQ_LINK(MIO_CWQ_TAIL(cwq), (mio_q_t*)x, cwq) #define MIO_CWQ_DEQ(cwq) MIO_CWQ_UNLINK(MIO_CWQ_HEAD(cwq)) /* write queue */ struct mio_wq_t { mio_wq_t* q_next; mio_wq_t* q_prev; mio_iolen_t olen; /* original data length */ mio_uint8_t* ptr; /* pointer to data */ mio_iolen_t len; /* remaining data length */ void* ctx; mio_dev_t* dev; /* back-pointer to the device */ mio_tmridx_t tmridx; mio_devaddr_t dstaddr; }; #define MIO_WQ_INIT(wq) ((wq)->q_next = (wq)->q_prev = (wq)) #define MIO_WQ_TAIL(wq) ((wq)->q_prev) #define MIO_WQ_HEAD(wq) ((wq)->q_next) #define MIO_WQ_IS_EMPTY(wq) (MIO_WQ_HEAD(wq) == (wq)) #define MIO_WQ_IS_NODE(wq,x) ((wq) != (x)) #define MIO_WQ_IS_HEAD(wq,x) (MIO_WQ_HEAD(wq) == (x)) #define MIO_WQ_IS_TAIL(wq,x) (MIO_WQ_TAIL(wq) == (x)) #define MIO_WQ_NEXT(x) ((x)->q_next) #define MIO_WQ_PREV(x) ((x)->q_prev) #define MIO_WQ_LINK(p,x,n) MIO_Q_LINK((mio_q_t*)p,(mio_q_t*)x,(mio_q_t*)n) #define MIO_WQ_UNLINK(x) MIO_Q_UNLINK((mio_q_t*)x) #define MIO_WQ_REPL(o,n) MIO_Q_REPL(o,n); #define MIO_WQ_ENQ(wq,x) MIO_WQ_LINK(MIO_WQ_TAIL(wq), (mio_q_t*)x, wq) #define MIO_WQ_DEQ(wq) MIO_WQ_UNLINK(MIO_WQ_HEAD(wq)) #define MIO_DEV_HEADER \ mio_t* mio; \ mio_oow_t dev_size; \ int dev_cap; \ mio_dev_mth_t* dev_mth; \ mio_dev_evcb_t* dev_evcb; \ mio_ntime_t rtmout; \ mio_tmridx_t rtmridx; \ mio_wq_t wq; \ mio_oow_t cw_count; \ mio_dev_t* dev_prev; \ mio_dev_t* dev_next struct mio_dev_t { MIO_DEV_HEADER; }; #define MIO_DEVL_PREPEND_DEV(lh,dev) do { \ (dev)->dev_prev = (lh); \ (dev)->dev_next = (lh)->dev_next; \ (dev)->dev_next->dev_prev = (dev); \ (lh)->dev_next = (dev); \ } while(0) #define MIO_DEVL_APPEND_DEV(lh,dev) do { \ (dev)->dev_next = (lh); \ (dev)->dev_prev = (lh)->dev_prev; \ (dev)->dev_prev->dev_next = (dev); \ (lh)->dev_prev = (dev); \ } while(0) #define MIO_DEVL_UNLINK_DEV(dev) do { \ (dev)->dev_prev->dev_next = (dev)->dev_next; \ (dev)->dev_next->dev_prev = (dev)->dev_prev; \ } while (0) #define MIO_DEVL_INIT(lh) ((lh)->dev_next = (lh)->dev_prev = lh) #define MIO_DEVL_FIRST_DEV(lh) ((lh)->dev_next) #define MIO_DEVL_LAST_DEV(lh) ((lh)->dev_prev) #define MIO_DEVL_IS_EMPTY(lh) (MIO_DEVL_FIRST_DEV(lh) == (lh)) #define MIO_DEVL_IS_NIL_DEV(lh,dev) ((dev) == (lh)) enum mio_dev_cap_t { MIO_DEV_CAP_VIRTUAL = (1 << 0), MIO_DEV_CAP_IN = (1 << 1), MIO_DEV_CAP_OUT = (1 << 2), MIO_DEV_CAP_PRI = (1 << 3), /* meaningful only if #MIO_DEV_CAP_IN is set */ MIO_DEV_CAP_STREAM = (1 << 4), MIO_DEV_CAP_IN_DISABLED = (1 << 5), MIO_DEV_CAP_OUT_UNQUEUEABLE = (1 << 6), MIO_DEV_CAP_ALL_MASK = (MIO_DEV_CAP_VIRTUAL | MIO_DEV_CAP_IN | MIO_DEV_CAP_OUT | MIO_DEV_CAP_PRI | MIO_DEV_CAP_STREAM | MIO_DEV_CAP_IN_DISABLED | MIO_DEV_CAP_OUT_UNQUEUEABLE), /* ------------------------------------------------------------------- * the followings bits are for internal use only. * never set these bits to the dev_cap field. * ------------------------------------------------------------------- */ MIO_DEV_CAP_IN_CLOSED = (1 << 10), MIO_DEV_CAP_OUT_CLOSED = (1 << 11), MIO_DEV_CAP_IN_WATCHED = (1 << 12), MIO_DEV_CAP_OUT_WATCHED = (1 << 13), MIO_DEV_CAP_PRI_WATCHED = (1 << 14), /**< can be set only if MIO_DEV_CAP_IN_WATCHED is set */ MIO_DEV_CAP_ACTIVE = (1 << 15), MIO_DEV_CAP_HALTED = (1 << 16), MIO_DEV_CAP_ZOMBIE = (1 << 17), MIO_DEV_CAP_RENEW_REQUIRED = (1 << 18), MIO_DEV_CAP_WATCH_STARTED = (1 << 19), MIO_DEV_CAP_WATCH_SUSPENDED = (1 << 20) }; typedef enum mio_dev_cap_t mio_dev_cap_t; enum mio_dev_watch_cmd_t { MIO_DEV_WATCH_START, MIO_DEV_WATCH_UPDATE, MIO_DEV_WATCH_RENEW, /* automatic renewal */ MIO_DEV_WATCH_STOP }; typedef enum mio_dev_watch_cmd_t mio_dev_watch_cmd_t; enum mio_dev_event_t { MIO_DEV_EVENT_IN = (1 << 0), MIO_DEV_EVENT_OUT = (1 << 1), MIO_DEV_EVENT_PRI = (1 << 2), MIO_DEV_EVENT_HUP = (1 << 3), MIO_DEV_EVENT_ERR = (1 << 4) }; typedef enum mio_dev_event_t mio_dev_event_t; #define MIO_CWQFL_SIZE 16 #define MIO_CWQFL_ALIGN 16 /* ========================================================================= * CHECK-AND-FREE MEMORY BLOCK * ========================================================================= */ #define MIO_CFMB_HEADER \ mio_t* mio; \ mio_cfmb_t* cfmb_next; \ mio_cfmb_t* cfmb_prev; \ mio_cfmb_checker_t cfmb_checker typedef struct mio_cfmb_t mio_cfmb_t; typedef int (*mio_cfmb_checker_t) ( mio_t* mio, mio_cfmb_t* cfmb ); struct mio_cfmb_t { MIO_CFMB_HEADER; }; #define MIO_CFMBL_PREPEND_CFMB(lh,cfmb) do { \ (cfmb)->cfmb_prev = (lh); \ (cfmb)->cfmb_next = (lh)->cfmb_next; \ (cfmb)->cfmb_next->cfmb_prev = (cfmb); \ (lh)->cfmb_next = (cfmb); \ } while(0) #define MIO_CFMBL_APPEND_CFMB(lh,cfmb) do { \ (cfmb)->cfmb_next = (lh); \ (cfmb)->cfmb_prev = (lh)->cfmb_prev; \ (cfmb)->cfmb_prev->cfmb_next = (cfmb); \ (lh)->cfmb_prev = (cfmb); \ } while(0) #define MIO_CFMBL_UNLINK_CFMB(cfmb) do { \ (cfmb)->cfmb_prev->cfmb_next = (cfmb)->cfmb_next; \ (cfmb)->cfmb_next->cfmb_prev = (cfmb)->cfmb_prev; \ } while (0) #define MIO_CFMBL_INIT(lh) ((lh)->cfmb_next = (lh)->cfmb_prev = lh) #define MIO_CFMBL_FIRST_CFMB(lh) ((lh)->cfmb_next) #define MIO_CFMBL_LAST_CFMB(lh) ((lh)->cfmb_prev) #define MIO_CFMBL_IS_EMPTY(lh) (MIO_CFMBL_FIRST_CFMB(lh) == (lh)) #define MIO_CFMBL_IS_NIL_CFMB(lh,cfmb) ((cfmb) == (lh)) #define MIO_CFMBL_PREV_CFMB(cfmb) ((cfmb)->cfmb_prev) #define MIO_CFMBL_NEXT_CFMB(cfmb) ((cfmb)->cfmb_next) /* ========================================================================= * SERVICE * ========================================================================= */ typedef void (*mio_svc_stop_t) (mio_svc_t* svc); #define MIO_SVC_HEADER \ mio_t* mio; \ mio_svc_stop_t svc_stop; \ mio_svc_t* svc_prev; \ mio_svc_t* svc_next /* the stop callback is called if it's not NULL and the service is still * alive when mio_close() is reached. it still calls MIO_SVCL_UNLINK_SVC() * if the stop callback is NULL. The stop callback, if specified, must * call MIO_SVCL_UNLINK_SVC(). */ struct mio_svc_t { MIO_SVC_HEADER; }; #define MIO_SVCL_PREPEND_SVC(lh,svc) do { \ (svc)->svc_prev = (lh); \ (svc)->svc_next = (lh)->svc_next; \ (svc)->svc_next->svc_prev = (svc); \ (lh)->svc_next = (svc); \ } while(0) #define MIO_SVCL_APPEND_SVC(lh,svc) do { \ (svc)->svc_next = (lh); \ (svc)->svc_prev = (lh)->svc_prev; \ (svc)->svc_prev->svc_next = (svc); \ (lh)->svc_prev = (svc); \ } while(0) #define MIO_SVCL_UNLINK_SVC(svc) do { \ (svc)->svc_prev->svc_next = (svc)->svc_next; \ (svc)->svc_next->svc_prev = (svc)->svc_prev; \ } while (0) #define MIO_SVCL_INIT(lh) ((lh)->svc_next = (lh)->svc_prev = lh) #define MIO_SVCL_FIRST_SVC(lh) ((lh)->svc_next) #define MIO_SVCL_LAST_SVC(lh) ((lh)->svc_prev) #define MIO_SVCL_IS_EMPTY(lh) (MIO_SVCL_FIRST_SVC(lh) == (lh)) #define MIO_SVCL_IS_NIL_SVC(lh,svc) ((svc) == (lh)) #define MIO_SVCL_PREV_SVC(svc) ((svc)->svc_prev) #define MIO_SVCL_NEXT_SVC(svc) ((svc)->svc_next) /* ========================================================================= * MIO LOGGING * ========================================================================= */ enum mio_log_mask_t { MIO_LOG_DEBUG = (1u << 0), MIO_LOG_INFO = (1u << 1), MIO_LOG_WARN = (1u << 2), MIO_LOG_ERROR = (1u << 3), MIO_LOG_FATAL = (1u << 4), MIO_LOG_UNTYPED = (1u << 6), /* only to be used by MIO_DEBUGx() and MIO_INFOx() */ MIO_LOG_CORE = (1u << 7), MIO_LOG_DEV = (1u << 8), MIO_LOG_TIMER = (1u << 9), MIO_LOG_ALL_LEVELS = (MIO_LOG_DEBUG | MIO_LOG_INFO | MIO_LOG_WARN | MIO_LOG_ERROR | MIO_LOG_FATAL), MIO_LOG_ALL_TYPES = (MIO_LOG_UNTYPED | MIO_LOG_CORE | MIO_LOG_DEV | MIO_LOG_TIMER), MIO_LOG_STDOUT = (1u << 14), /* write log messages to stdout without timestamp. MIO_LOG_STDOUT wins over MIO_LOG_STDERR. */ MIO_LOG_STDERR = (1u << 15) /* write log messages to stderr without timestamp. */ }; typedef enum mio_log_mask_t mio_log_mask_t; /* all bits must be set to get enabled */ #define MIO_LOG_ENABLED(mio,mask) (((mio)->option.log_mask & (mask)) == (mask)) #define MIO_LOG0(mio,mask,fmt) do { if (MIO_LOG_ENABLED(mio,mask)) mio_logbfmt(mio, mask, fmt); } while(0) #define MIO_LOG1(mio,mask,fmt,a1) do { if (MIO_LOG_ENABLED(mio,mask)) mio_logbfmt(mio, mask, fmt, a1); } while(0) #define MIO_LOG2(mio,mask,fmt,a1,a2) do { if (MIO_LOG_ENABLED(mio,mask)) mio_logbfmt(mio, mask, fmt, a1, a2); } while(0) #define MIO_LOG3(mio,mask,fmt,a1,a2,a3) do { if (MIO_LOG_ENABLED(mio,mask)) mio_logbfmt(mio, mask, fmt, a1, a2, a3); } while(0) #define MIO_LOG4(mio,mask,fmt,a1,a2,a3,a4) do { if (MIO_LOG_ENABLED(mio,mask)) mio_logbfmt(mio, mask, fmt, a1, a2, a3, a4); } while(0) #define MIO_LOG5(mio,mask,fmt,a1,a2,a3,a4,a5) do { if (MIO_LOG_ENABLED(mio,mask)) mio_logbfmt(mio, mask, fmt, a1, a2, a3, a4, a5); } while(0) #define MIO_LOG6(mio,mask,fmt,a1,a2,a3,a4,a5,a6) do { if (MIO_LOG_ENABLED(mio,mask)) mio_logbfmt(mio, mask, fmt, a1, a2, a3, a4, a5, a6); } while(0) #if defined(MIO_BUILD_RELEASE) /* [NOTE] * get rid of debugging message totally regardless of * the log mask in the release build. */ # define MIO_DEBUG0(mio,fmt) # define MIO_DEBUG1(mio,fmt,a1) # define MIO_DEBUG2(mio,fmt,a1,a2) # define MIO_DEBUG3(mio,fmt,a1,a2,a3) # define MIO_DEBUG4(mio,fmt,a1,a2,a3,a4) # define MIO_DEBUG5(mio,fmt,a1,a2,a3,a4,a5) # define MIO_DEBUG6(mio,fmt,a1,a2,a3,a4,a5,a6) #else # define MIO_DEBUG0(mio,fmt) MIO_LOG0(mio, MIO_LOG_DEBUG | MIO_LOG_UNTYPED, fmt) # define MIO_DEBUG1(mio,fmt,a1) MIO_LOG1(mio, MIO_LOG_DEBUG | MIO_LOG_UNTYPED, fmt, a1) # define MIO_DEBUG2(mio,fmt,a1,a2) MIO_LOG2(mio, MIO_LOG_DEBUG | MIO_LOG_UNTYPED, fmt, a1, a2) # define MIO_DEBUG3(mio,fmt,a1,a2,a3) MIO_LOG3(mio, MIO_LOG_DEBUG | MIO_LOG_UNTYPED, fmt, a1, a2, a3) # define MIO_DEBUG4(mio,fmt,a1,a2,a3,a4) MIO_LOG4(mio, MIO_LOG_DEBUG | MIO_LOG_UNTYPED, fmt, a1, a2, a3, a4) # define MIO_DEBUG5(mio,fmt,a1,a2,a3,a4,a5) MIO_LOG5(mio, MIO_LOG_DEBUG | MIO_LOG_UNTYPED, fmt, a1, a2, a3, a4, a5) # define MIO_DEBUG6(mio,fmt,a1,a2,a3,a4,a5,a6) MIO_LOG6(mio, MIO_LOG_DEBUG | MIO_LOG_UNTYPED, fmt, a1, a2, a3, a4, a5, a6) #endif #define MIO_INFO0(mio,fmt) MIO_LOG0(mio, MIO_LOG_INFO | MIO_LOG_UNTYPED, fmt) #define MIO_INFO1(mio,fmt,a1) MIO_LOG1(mio, MIO_LOG_INFO | MIO_LOG_UNTYPED, fmt, a1) #define MIO_INFO2(mio,fmt,a1,a2) MIO_LOG2(mio, MIO_LOG_INFO | MIO_LOG_UNTYPED, fmt, a1, a2) #define MIO_INFO3(mio,fmt,a1,a2,a3) MIO_LOG3(mio, MIO_LOG_INFO | MIO_LOG_UNTYPED, fmt, a1, a2, a3) #define MIO_INFO4(mio,fmt,a1,a2,a3,a4) MIO_LOG4(mio, MIO_LOG_INFO | MIO_LOG_UNTYPED, fmt, a1, a2, a3, a4) #define MIO_INFO5(mio,fmt,a1,a2,a3,a4,a5) MIO_LOG5(mio, MIO_LOG_INFO | MIO_LOG_UNTYPED, fmt, a1, a2, a3, a4, a5) #define MIO_INFO6(mio,fmt,a1,a2,a3,a4,a5,a6) MIO_LOG6(mio, MIO_LOG_INFO | MIO_LOG_UNTYPED, fmt, a1, a2, a3, a4, a5, a6) /* ========================================================================= */ enum mio_sys_mux_cmd_t { MIO_SYS_MUX_CMD_INSERT = 0, MIO_SYS_MUX_CMD_UPDATE = 1, MIO_SYS_MUX_CMD_DELETE = 2 }; typedef enum mio_sys_mux_cmd_t mio_sys_mux_cmd_t; typedef void (*mio_sys_mux_evtcb_t) ( mio_t* mio, mio_dev_t* dev, int events, int rdhup ); typedef struct mio_sys_t mio_sys_t; struct mio_t { mio_oow_t _instsize; mio_mmgr_t* _mmgr; mio_cmgr_t* _cmgr; mio_errnum_t errnum; struct { union { mio_ooch_t ooch[MIO_ERRMSG_CAPA]; mio_bch_t bch[MIO_ERRMSG_CAPA]; mio_uch_t uch[MIO_ERRMSG_CAPA]; } tmpbuf; mio_ooch_t buf[MIO_ERRMSG_CAPA]; mio_oow_t len; } errmsg; unsigned short int _shuterr; unsigned short int _fini_in_progress; struct { mio_bitmask_t trait; mio_bitmask_t log_mask; mio_oow_t log_maxcapa; } option; struct { mio_ooch_t* ptr; mio_oow_t len; mio_oow_t capa; mio_bitmask_t last_mask; mio_bitmask_t default_type_mask; } log; struct { struct { mio_ooch_t* ptr; mio_oow_t capa; mio_oow_t len; } xbuf; /* buffer to support sprintf */ } sprintf; mio_stopreq_t stopreq; /* stop request to abort mio_loop() */ mio_cfmb_t cfmb; /* list head of cfmbs */ mio_dev_t actdev; /* list head of active devices */ mio_dev_t hltdev; /* list head of halted devices */ mio_dev_t zmbdev; /* list head of zombie devices */ mio_uint8_t bigbuf[65535]; /* TODO: make this dynamic depending on devices added. device may indicate a buffer size required??? */ mio_ntime_t init_time; struct { mio_oow_t capa; mio_oow_t size; mio_tmrjob_t* jobs; } tmr; mio_cwq_t cwq; mio_cwq_t* cwqfl[MIO_CWQFL_SIZE]; /* list of free cwq objects */ mio_svc_t actsvc; /* list head of active services */ /* platform specific fields below */ mio_sys_t* sysdep; }; /* ========================================================================= */ #ifdef __cplusplus extern "C" { #endif MIO_EXPORT mio_t* mio_open ( mio_mmgr_t* mmgr, mio_oow_t xtnsize, mio_cmgr_t* cmgr, mio_oow_t tmrcapa, /**< initial timer capacity */ mio_errinf_t* errinf ); MIO_EXPORT void mio_close ( mio_t* mio ); MIO_EXPORT int mio_init ( mio_t* mio, mio_mmgr_t* mmgr, mio_cmgr_t* cmgr, mio_oow_t tmrcapa ); MIO_EXPORT void mio_fini ( mio_t* mio ); MIO_EXPORT int mio_getoption ( mio_t* mio, mio_option_t id, void* value ); MIO_EXPORT int mio_setoption ( mio_t* mio, mio_option_t id, const void* value ); #if defined(MIO_HAVE_INLINE) static MIO_INLINE void* mio_getxtn (mio_t* mio) { return (void*)((mio_uint8_t*)mio + mio->_instsize); } static MIO_INLINE mio_mmgr_t* mio_getmmgr (mio_t* mio) { return mio->_mmgr; } static MIO_INLINE mio_cmgr_t* mio_getcmgr (mio_t* mio) { return mio->_cmgr; } static MIO_INLINE void mio_setcmgr (mio_t* mio, mio_cmgr_t* cmgr) { mio->_cmgr = cmgr; } static MIO_INLINE mio_errnum_t mio_geterrnum (mio_t* mio) { return mio->errnum; } #else # define mio_getxtn(mio) ((void*)((mio_uint8_t*)mio + ((mio_t*)mio)->_instsize)) # define mio_getmmgr(mio) (((mio_t*)(mio))->_mmgr) # define mio_getcmgr(mio) (((mio_t*)(mio))->_cmgr) # define mio_setcmgr(mio,cmgr) (((mio_t*)(mio))->_cmgr = (cmgr)) # define mio_geterrnum(mio) (((mio_t*)(mio))->errnum) #endif MIO_EXPORT void mio_seterrnum ( mio_t* mio, mio_errnum_t errnum ); MIO_EXPORT void mio_seterrwithsyserr ( mio_t* mio, int syserr_type, int syserr_code ); MIO_EXPORT void mio_seterrbfmt ( mio_t* mio, mio_errnum_t errnum, const mio_bch_t* fmt, ... ); MIO_EXPORT void mio_seterrufmt ( mio_t* mio, mio_errnum_t errnum, const mio_uch_t* fmt, ... ); MIO_EXPORT void mio_seterrbfmtv ( mio_t* mio, mio_errnum_t errnum, const mio_bch_t* fmt, va_list ap ); MIO_EXPORT void mio_seterrufmtv ( mio_t* mio, mio_errnum_t errnum, const mio_uch_t* fmt, va_list ap ); MIO_EXPORT void mio_seterrbfmtwithsyserr ( mio_t* mio, int syserr_type, int syserr_code, const mio_bch_t* fmt, ... ); MIO_EXPORT void mio_seterrufmtwithsyserr ( mio_t* mio, int syserr_type, int syserr_code, const mio_uch_t* fmt, ... ); MIO_EXPORT const mio_ooch_t* mio_geterrstr ( mio_t* mio ); MIO_EXPORT const mio_ooch_t* mio_geterrmsg ( mio_t* mio ); MIO_EXPORT void mio_geterrinf ( mio_t* mio, mio_errinf_t* info ); MIO_EXPORT const mio_ooch_t* mio_backuperrmsg ( mio_t* mio ); MIO_EXPORT int mio_exec ( mio_t* mio ); MIO_EXPORT int mio_loop ( mio_t* mio ); MIO_EXPORT void mio_stop ( mio_t* mio, mio_stopreq_t stopreq ); MIO_EXPORT mio_dev_t* mio_dev_make ( mio_t* mio, mio_oow_t dev_size, mio_dev_mth_t* dev_mth, mio_dev_evcb_t* dev_evcb, void* make_ctx ); MIO_EXPORT void mio_dev_kill ( mio_dev_t* dev ); MIO_EXPORT void mio_dev_halt ( mio_dev_t* dev ); #if defined(MIO_HAVE_INLINE) static MIO_INLINE mio_t* mio_dev_getmio (mio_dev_t* dev) { return dev->mio; } #else # define mio_dev_getmio(dev) (((mio_dev_t*)(dev))->mio) #endif MIO_EXPORT int mio_dev_ioctl ( mio_dev_t* dev, int cmd, void* arg ); MIO_EXPORT int mio_dev_watch ( mio_dev_t* dev, mio_dev_watch_cmd_t cmd, /** 0 or bitwise-ORed of #MIO_DEV_EVENT_IN and #MIO_DEV_EVENT_OUT */ int events ); MIO_EXPORT int mio_dev_read ( mio_dev_t* dev, int enabled ); /* * The mio_dev_timedread() function enables or disables the input watching. * If tmout is not MIO_NULL, it schedules to fire the on_read() callback * with the length of -1 and the mio error number set to MIO_ETMOUT. * If there is input before the time elapses, the scheduled timer job * is automaticaly cancelled. A call to mio_dev_read() or mio_dev_timedread() * with no timeout also cancels the unfired scheduled job. */ MIO_EXPORT int mio_dev_timedread ( mio_dev_t* dev, int enabled, const mio_ntime_t* tmout ); /** * The mio_dev_write() function posts a writing request. * It attempts to write data immediately if there is no pending requests. * If writing fails, it returns -1. If writing succeeds, it calls the * on_write callback. If the callback fails, it returns -1. If the callback * succeeds, it returns 1. If no immediate writing is possible, the request * is enqueued to a pending request list. If enqueing gets successful, * it returns 0. otherwise it returns -1. */ MIO_EXPORT int mio_dev_write ( mio_dev_t* dev, const void* data, mio_iolen_t len, void* wrctx, const mio_devaddr_t* dstaddr ); MIO_EXPORT int mio_dev_writev ( mio_dev_t* dev, mio_iovec_t* iov, mio_iolen_t iovcnt, void* wrctx, const mio_devaddr_t* dstaddr ); MIO_EXPORT int mio_dev_timedwrite ( mio_dev_t* dev, const void* data, mio_iolen_t len, const mio_ntime_t* tmout, void* wrctx, const mio_devaddr_t* dstaddr ); MIO_EXPORT int mio_dev_timedwritev ( mio_dev_t* dev, mio_iovec_t* iov, mio_iolen_t iovcnt, const mio_ntime_t* tmout, void* wrctx, const mio_devaddr_t* dstaddr ); /* ========================================================================= * SERVICE * ========================================================================= */ #if defined(MIO_HAVE_INLINE) static MIO_INLINE mio_t* mio_svc_getmio (mio_svc_t* svc) { return svc->mio; } #else # define mio_svc_getmio(svc) (((mio_svc_t*)(svc))->mio) #endif /* ========================================================================= * TIMER MANAGEMENT * ========================================================================= */ /** * The mio_instmrjob() function schedules a new event. * * \return #MIO_TMRIDX_INVALID on failure, valid index on success. */ MIO_EXPORT mio_tmridx_t mio_instmrjob ( mio_t* mio, const mio_tmrjob_t* job ); MIO_EXPORT mio_tmridx_t mio_updtmrjob ( mio_t* mio, mio_tmridx_t index, const mio_tmrjob_t* job ); MIO_EXPORT void mio_deltmrjob ( mio_t* mio, mio_tmridx_t index ); /** * The mio_gettmrjob() function returns the * pointer to the registered event at the given index. */ MIO_EXPORT mio_tmrjob_t* mio_gettmrjob ( mio_t* mio, mio_tmridx_t index ); MIO_EXPORT int mio_gettmrjobdeadline ( mio_t* mio, mio_tmridx_t index, mio_ntime_t* deadline ); /* ========================================================================= * TIME * ========================================================================= */ /** * the mio_gettime() function returns the elapsed time since mio initialization. */ MIO_EXPORT void mio_gettime ( mio_t* mio, mio_ntime_t* now ); /* ========================================================================= * SYSTEM MEMORY MANAGEMENT FUCNTIONS VIA MMGR * ========================================================================= */ MIO_EXPORT void* mio_allocmem ( mio_t* mio, mio_oow_t size ); MIO_EXPORT void* mio_callocmem ( mio_t* mio, mio_oow_t size ); MIO_EXPORT void* mio_reallocmem ( mio_t* mio, void* ptr, mio_oow_t size ); MIO_EXPORT void mio_freemem ( mio_t* mio, void* ptr ); MIO_EXPORT void mio_addcfmb ( mio_t* mio, mio_cfmb_t* cfmb, mio_cfmb_checker_t checker ); /* ========================================================================= * STRING ENCODING CONVERSION * ========================================================================= */ #if defined(MIO_OOCH_IS_UCH) # define mio_convootobchars(mio,oocs,oocslen,bcs,bcslen) mio_convutobchars(mio,oocs,oocslen,bcs,bcslen) # define mio_convbtooochars(mio,bcs,bcslen,oocs,oocslen) mio_convbtouchars(mio,bcs,bcslen,oocs,oocslen) # define mio_convootobcstr(mio,oocs,oocslen,bcs,bcslen) mio_convutobcstr(mio,oocs,oocslen,bcs,bcslen) # define mio_convbtooocstr(mio,bcs,bcslen,oocs,oocslen) mio_convbtoucstr(mio,bcs,bcslen,oocs,oocslen) #else # define mio_convootouchars(mio,oocs,oocslen,bcs,bcslen) mio_convbtouchars(mio,oocs,oocslen,bcs,bcslen) # define mio_convutooochars(mio,bcs,bcslen,oocs,oocslen) mio_convutobchars(mio,bcs,bcslen,oocs,oocslen) # define mio_convootoucstr(mio,oocs,oocslen,bcs,bcslen) mio_convbtoucstr(mio,oocs,oocslen,bcs,bcslen) # define mio_convutooocstr(mio,bcs,bcslen,oocs,oocslen) mio_convutobcstr(mio,bcs,bcslen,oocs,oocslen) #endif MIO_EXPORT int mio_convbtouchars ( mio_t* mio, const mio_bch_t* bcs, mio_oow_t* bcslen, mio_uch_t* ucs, mio_oow_t* ucslen, int all ); MIO_EXPORT int mio_convutobchars ( mio_t* mio, const mio_uch_t* ucs, mio_oow_t* ucslen, mio_bch_t* bcs, mio_oow_t* bcslen ); /** * The mio_convbtoucstr() function converts a null-terminated byte string * to a wide string. */ MIO_EXPORT int mio_convbtoucstr ( mio_t* mio, const mio_bch_t* bcs, mio_oow_t* bcslen, mio_uch_t* ucs, mio_oow_t* ucslen, int all ); /** * The mio_convutobcstr() function converts a null-terminated wide string * to a byte string. */ MIO_EXPORT int mio_convutobcstr ( mio_t* mio, const mio_uch_t* ucs, mio_oow_t* ucslen, mio_bch_t* bcs, mio_oow_t* bcslen ); #if defined(MIO_OOCH_IS_UCH) # define mio_dupootobcharswithheadroom(mio,hrb,oocs,oocslen,bcslen) mio_duputobcharswithheadroom(mio,hrb,oocs,oocslen,bcslen) # define mio_dupbtooocharswithheadroom(mio,hrb,bcs,bcslen,oocslen,all) mio_dupbtoucharswithheadroom(mio,hrb,bcs,bcslen,oocslen,all) # define mio_dupootobchars(mio,oocs,oocslen,bcslen) mio_duputobchars(mio,oocs,oocslen,bcslen) # define mio_dupbtooochars(mio,bcs,bcslen,oocslen,all) mio_dupbtouchars(mio,bcs,bcslen,oocslen,all) # define mio_dupootobcstrwithheadroom(mio,hrb,oocs,bcslen) mio_duputobcstrwithheadroom(mio,hrb,oocs,bcslen) # define mio_dupbtooocstrwithheadroom(mio,hrb,bcs,oocslen,all) mio_dupbtoucstrwithheadroom(mio,hrb,bcs,oocslen,all) # define mio_dupootobcstr(mio,oocs,bcslen) mio_duputobcstr(mio,oocs,bcslen) # define mio_dupbtooocstr(mio,bcs,oocslen,all) mio_dupbtoucstr(mio,bcs,oocslen,all) #else # define mio_dupootoucharswithheadroom(mio,hrb,oocs,oocslen,ucslen,all) mio_dupbtoucharswithheadroom(mio,hrb,oocs,oocslen,ucslen,all) # define mio_duputooocharswithheadroom(mio,hrb,ucs,ucslen,oocslen) mio_duputobcharswithheadroom(mio,hrb,ucs,ucslen,oocslen) # define mio_dupootouchars(mio,oocs,oocslen,ucslen,all) mio_dupbtouchars(mio,oocs,oocslen,ucslen,all) # define mio_duputooochars(mio,ucs,ucslen,oocslen) mio_duputobchars(mio,ucs,ucslen,oocslen) # define mio_dupootoucstrwithheadroom(mio,hrb,oocs,ucslen,all) mio_dupbtoucstrwithheadroom(mio,hrb,oocs,ucslen,all) # define mio_duputooocstrwithheadroom(mio,hrb,ucs,oocslen) mio_duputobcstrwithheadroom(mio,hrb,ucs,oocslen) # define mio_dupootoucstr(mio,oocs,ucslen,all) mio_dupbtoucstr(mio,oocs,ucslen,all) # define mio_duputooocstr(mio,ucs,oocslen) mio_duputobcstr(mio,ucs,oocslen) #endif MIO_EXPORT mio_uch_t* mio_dupbtoucharswithheadroom ( mio_t* mio, mio_oow_t headroom_bytes, const mio_bch_t* bcs, mio_oow_t bcslen, mio_oow_t* ucslen, int all ); MIO_EXPORT mio_bch_t* mio_duputobcharswithheadroom ( mio_t* mio, mio_oow_t headroom_bytes, const mio_uch_t* ucs, mio_oow_t ucslen, mio_oow_t* bcslen ); MIO_EXPORT mio_uch_t* mio_dupbtouchars ( mio_t* mio, const mio_bch_t* bcs, mio_oow_t bcslen, mio_oow_t* ucslen, int all ); MIO_EXPORT mio_bch_t* mio_duputobchars ( mio_t* mio, const mio_uch_t* ucs, mio_oow_t ucslen, mio_oow_t* bcslen ); MIO_EXPORT mio_uch_t* mio_dupbtoucstrwithheadroom ( mio_t* mio, mio_oow_t headroom_bytes, const mio_bch_t* bcs, mio_oow_t* ucslen, int all ); MIO_EXPORT mio_bch_t* mio_duputobcstrwithheadroom ( mio_t* mio, mio_oow_t headroom_bytes, const mio_uch_t* ucs, mio_oow_t* bcslen ); MIO_EXPORT mio_uch_t* mio_dupbtoucstr ( mio_t* mio, const mio_bch_t* bcs, mio_oow_t* ucslen, /* optional: length of returned string */ int all ); MIO_EXPORT mio_bch_t* mio_duputobcstr ( mio_t* mio, const mio_uch_t* ucs, mio_oow_t* bcslen /* optional: length of returned string */ ); MIO_EXPORT mio_uch_t* mio_dupuchars ( mio_t* mio, const mio_uch_t* ucs, mio_oow_t ucslen ); MIO_EXPORT mio_bch_t* mio_dupbchars ( mio_t* mio, const mio_bch_t* bcs, mio_oow_t bcslen ); MIO_EXPORT mio_uch_t* mio_dupucstr ( mio_t* mio, const mio_uch_t* ucs, mio_oow_t* ucslen /* [OUT] length*/ ); MIO_EXPORT mio_bch_t* mio_dupbcstr ( mio_t* mio, const mio_bch_t* bcs, mio_oow_t* bcslen /* [OUT] length */ ); MIO_EXPORT mio_uch_t* mio_dupucstrs ( mio_t* mio, const mio_uch_t* ucs[], mio_oow_t* ucslen ); MIO_EXPORT mio_bch_t* mio_dupbcstrs ( mio_t* mio, const mio_bch_t* bcs[], mio_oow_t* bcslen ); #if defined(MIO_OOCH_IS_UCH) # define mio_dupoochars(mio,oocs,oocslen) mio_dupuchars(mio,oocs,oocslen) # define mio_dupoocstr(mio,oocs,oocslen) mio_dupucstr(mio,oocs,oocslen) #else # define mio_dupoochars(mio,oocs,oocslen) mio_dupbchars(mio,oocs,oocslen) # define mio_dupoocstr(mio,oocs,oocslen) mio_dupbcstr(mio,oocs,oocslen) #endif /* ========================================================================= * STRING FORMATTING * ========================================================================= */ MIO_EXPORT mio_oow_t mio_vfmttoucstr ( mio_t* mio, mio_uch_t* buf, mio_oow_t bufsz, const mio_uch_t* fmt, va_list ap ); MIO_EXPORT mio_oow_t mio_fmttoucstr ( mio_t* mio, mio_uch_t* buf, mio_oow_t bufsz, const mio_uch_t* fmt, ... ); MIO_EXPORT mio_oow_t mio_vfmttobcstr ( mio_t* mio, mio_bch_t* buf, mio_oow_t bufsz, const mio_bch_t* fmt, va_list ap ); MIO_EXPORT mio_oow_t mio_fmttobcstr ( mio_t* mio, mio_bch_t* buf, mio_oow_t bufsz, const mio_bch_t* fmt, ... ); #if defined(MIO_OOCH_IS_UCH) # define mio_vfmttooocstr mio_vfmttoucstr # define mio_fmttooocstr mio_fmttoucstr #else # define mio_vfmttooocstr mio_vfmttobcstr # define mio_fmttooocstr mio_fmttobcstr #endif /* ========================================================================= * MIO VM LOGGING * ========================================================================= */ MIO_EXPORT mio_ooi_t mio_logbfmt ( mio_t* mio, mio_bitmask_t mask, const mio_bch_t* fmt, ... ); MIO_EXPORT mio_ooi_t mio_logufmt ( mio_t* mio, mio_bitmask_t mask, const mio_uch_t* fmt, ... ); #if defined(MIO_OOCH_IS_UCH) # define mio_logoofmt mio_logufmt #else # define mio_logoofmt mio_logbfmt #endif /* ========================================================================= * MISCELLANEOUS HELPER FUNCTIONS * ========================================================================= */ MIO_EXPORT const mio_ooch_t* mio_errnum_to_errstr ( mio_errnum_t errnum ); #ifdef __cplusplus } #endif #endif