diff options
| author | Garrett D'Amore <garrett@damore.org> | 2016-12-10 22:12:08 -0800 |
|---|---|---|
| committer | Garrett D'Amore <garrett@damore.org> | 2016-12-10 22:12:08 -0800 |
| commit | e72c61989757d8c0700f8e48ea82a3d99cf327fc (patch) | |
| tree | 6e03d6a6993d7f85ea62b23ddc0c6491045fbe5f /src | |
| download | nng-e72c61989757d8c0700f8e48ea82a3d99cf327fc.tar.gz nng-e72c61989757d8c0700f8e48ea82a3d99cf327fc.tar.bz2 nng-e72c61989757d8c0700f8e48ea82a3d99cf327fc.zip | |
Initial commit. This is not going to be useful to you for anything.
Diffstat (limited to 'src')
| -rw-r--r-- | src/README.adoc | 20 | ||||
| -rw-r--r-- | src/core/message.c | 249 | ||||
| -rw-r--r-- | src/core/nng_impl.h | 64 | ||||
| -rw-r--r-- | src/core/panic.c | 86 | ||||
| -rw-r--r-- | src/core/platform.c | 33 | ||||
| -rw-r--r-- | src/core/snprintf.c | 36 | ||||
| -rw-r--r-- | src/nng.h | 426 | ||||
| -rw-r--r-- | src/platform/platform.h | 125 | ||||
| -rw-r--r-- | src/platform/posix/posix_alloc.h | 45 | ||||
| -rw-r--r-- | src/platform/posix/posix_debug.h | 48 | ||||
| -rw-r--r-- | src/platform/posix/posix_impl.h | 41 | ||||
| -rw-r--r-- | src/platform/posix/posix_synch.h | 143 | ||||
| -rw-r--r-- | src/platform/posix/posix_vsnprintf.h | 30 |
13 files changed, 1346 insertions, 0 deletions
diff --git a/src/README.adoc b/src/README.adoc new file mode 100644 index 00000000..7560e656 --- /dev/null +++ b/src/README.adoc @@ -0,0 +1,20 @@ +nng - nanomsg-NG +================ + +This repository represents a work in progress rewrite of the SP protocol +library called "libnanomsg". The work is being done by Garrett D'Amore, +and at this juncture he is *not* soliciting assistance. + +This is a work in progress, and is *not* for general use or publication. +When the library is ready for broader consumption, an announcement will +be posted on the nanomsg mailing list and website. + +If you are looking for the current production version of nanomsg, please +see the https://github.com/nanomsg/nanomsg site. + +Note that commit histories here are subject to change in the future -- +once the production reaches a point that we are ready to start using +issue tracking and so forth, we will compress the existing deltas into +a single commit. This is probably some number of weeks in the future. + + - Garrett D'Amore (Dec. 9, 2016) diff --git a/src/core/message.c b/src/core/message.c new file mode 100644 index 00000000..20ca6b80 --- /dev/null +++ b/src/core/message.c @@ -0,0 +1,249 @@ +/* + * Copyright 2016 Garrett D'Amore <garrett@damore.org> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include <stdlib.h> +#include <string.h> + +#include "../nng.h" + +#include "nng_impl.h" + +/* + * Message API. + */ + +/* Message chunk, internal to the message implementation. */ +typedef struct { + size_t ch_cap; /* allocated size */ + size_t ch_len; /* length in use */ + char *ch_buf; /* underlying buffer */ + char *ch_ptr; /* pointer to actual data */ +} chunk_t; + +/* Underlying message chunk. */ +struct nng_msg { + chunk_t m_header; + chunk_t m_body; + int64_t m_expire; /* Unix usec */ + nng_pipe_t m_pipe; /* Pipe message was received on */ +}; + +/* + * chunk_grow increases the underlying space for a chunk. It ensures + * that the desired amount of trailing space (including the length) + * and headroom (excluding the length) are available. It also copies + * any extant referenced data. Note that the capacity will increase, + * but not the length. To increase the length of the referenced data, + * use either chunk_append or chunk_prepend. + * + * Note that having some headroom is useful when data must be prepended + * to a message - it avoids having to perform extra data copies, so we + * encourage initial allocations to start with sufficient room. + */ +static int +chunk_grow(chunk_t *ch, size_t newsz, size_t headwanted) +{ + size_t headroom = 0; + char *newbuf; + + /* + * We assume that if the pointer is a valid pointer, and inside + * the backing store, then the entire data length fits. In this + * case we perform a logical realloc, except we don't copy any + * unreferenced data. We do preserve the headroom of the previous + * use, since that may be there for a reason. + * + * The test below also covers the case where the pointers are both + * NULL, or the capacity is zero. + */ + + if ((ch->ch_ptr >= ch->ch_buf) && + (ch->ch_ptr < (ch->ch_buf + ch->ch_cap))) { + + headroom = (size_t)(ch->ch_ptr - ch->ch_buf); + if (((newsz + headwanted) < ch->ch_cap) && + (headwanted <= headroom)) { + /* We have enough space at the ends already. */ + return (0); + } + if (headwanted < headroom) { + /* We never shrink... headroom either. */ + headwanted = headroom; + } + if ((newbuf = nni_alloc(newsz + headwanted)) == NULL) { + return (NNG_ENOMEM); + } + /* Copy all the data, but not header or trailer. */ + memcpy(newbuf + headwanted, ch->ch_buf + headroom, ch->ch_len); + nni_free(ch->ch_buf, ch->ch_cap); + ch->ch_buf = newbuf; + ch->ch_ptr = newbuf + headwanted; + ch->ch_cap = newsz + headwanted; + return (0); + } + + /* + * We either don't have a data pointer yet, or it doesn't reference + * the backing store. In this case, we just check against the + * allocated capacity and grow, or don't grow. + */ + if (newsz > ch->ch_cap) { + /* Enough space at end, so just use it. */ + if (ch->ch_ptr == NULL) { + ch->ch_ptr = ch->ch_buf; + } + return (0); + + } else if ((newbuf = nni_alloc(newsz)) == NULL) { + return (NNG_ENOMEM); + } + + nni_free(ch->ch_buf, ch->ch_cap); + ch->ch_buf = newbuf; + ch->ch_cap = newsz; + if (ch->ch_ptr == NULL) { + ch->ch_ptr = ch->ch_buf; + } + return (0); +} + +static void +chunk_free(chunk_t *ch) +{ + if ((ch->ch_cap != 0) && (ch->ch_buf != NULL)) { + nni_free(ch->ch_buf, ch->ch_cap); + } + ch->ch_ptr = NULL; + ch->ch_buf = NULL; + ch->ch_len = 0; + ch->ch_cap = 0; +} + +/* chunk_trunc truncates the number of bytes from the end of the chunk. */ +static int +chunk_trunc(chunk_t *ch, size_t len) +{ + if (ch->ch_len < len) { + return (NNG_EINVAL); + } + ch->ch_len -= len; + return (0); +} + +/* chunk_trim removes the number of bytes from the beginning of the chunk. */ +static int +chunk_trim(chunk_t *ch, size_t len) +{ + if (ch->ch_len < len) { + return (NNG_EINVAL); + } + ch->ch_ptr += len; + ch->ch_len -= len; + return (0); +} + +/* + * chunk_append appends the data to the chunk, growing the size as necessary. + * If the data pointer is NULL, then the chunk data region is allocated, but + * uninitialized. + */ +static int +chunk_append(chunk_t *ch, const char *data, size_t len) +{ + int rv; + if (len == 0) { + return (0); + } + if ((rv = chunk_grow(ch, len + ch->ch_len, 0)) != 0) { + return (rv); + } + if (ch->ch_ptr == NULL) { + ch->ch_ptr = ch->ch_buf; + } + if (data != NULL) { + memcpy(ch->ch_ptr + ch->ch_len, data, len); + } + ch->ch_len += len; + return (0); +} + +/* + * chunk_prepend prepends data to the chunk, as efficiently as possible. + * If the data pointer is NULL, then no data is actually copied, but the + * data region will have "grown" in the beginning, with uninitialized data. + */ +static int +chunk_prepend(chunk_t *ch, const char *data, size_t len) +{ + int rv; + char *newbuf; + size_t headroom = 0; + + if (ch->ch_ptr == NULL) { + ch->ch_ptr = ch->ch_buf; + } + + if ((ch->ch_ptr >= ch->ch_buf) && + (ch->ch_ptr < (ch->ch_buf + ch->ch_cap)) && + (len <= (size_t)(ch->ch_ptr - ch->ch_buf))) { + /* There is already enough room at the beginning. */ + ch->ch_ptr -= len; + + } else if ((ch->ch_len + len) <= ch->ch_cap) { + /* We had enough capacity, just shuffle data down. */ + memmove(ch->ch_ptr + len, ch->ch_ptr, ch->ch_len); + + } else if ((rv = chunk_grow(ch, 0, len)) == 0) { + /* We grew the chunk, so adjust. */ + ch->ch_ptr -= len; + + } else { + /* Couldn't grow the chunk either. Error. */ + return (rv); + } + + ch->ch_len += len; + if (data) { + memcpy(ch->ch_ptr, data, len); + } + + return (0); +} + +#if 0 +NNG_DECL int nng_msg_alloc(nng_msg_t *, size_t); +NNG_DECL void nng_msg_free(nng_msg_t); +NNG_DECL int nng_msg_realloc(nng_mst_t, size_t); +NNG_DECL void *nng_msg_header(nng_msg_t, size_t *); +NNG_DECL void *nng_msg_body(nng_msg_t, size_t *); +NNG_DECL int nng_msg_port(nng_msg_t, nng_pipe_t *); + +NNG_DECL int nng_msg_append(nng_msg_t, const char *, size_t); +NNG_DECL int nng_msg_prepend(nng_msg_t, const char *, size_t); +NNG_DECL int nng_msg_trim(nng_msg_t, size_t); +NNG_DECL int nng_msg_trunc(nng_msg_t, size_t); + +NNG_DECL int nng_msg_append_header(nng_msg_t, const char *, size_t); +NNG_DECL int nng_msg_prepend_header(nng_msg_t, const char *, size_t); +NNG_DECL int nng_msg_trim_header(nng_msg_t, size_t); +NNG_DECL int nng_msg_trunc_header(nng_msg_t, size_t); +#endif diff --git a/src/core/nng_impl.h b/src/core/nng_impl.h new file mode 100644 index 00000000..a25b87a4 --- /dev/null +++ b/src/core/nng_impl.h @@ -0,0 +1,64 @@ +/* + * Copyright 2016 Garrett D'Amore <garrett@damore.org> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef NNG_IMPL_H +#define NNG_IMPL_H + +#include "nng.h" +#include "platform/platform.h" + +/* + * Internal implementation things for NNG, common definitions, etc. + * + * Hopefully it should be clear by the name that this file and its contents + * are *NOT* for use outside of this library. + * + * Symbols that are private to the library begin with the nni_ prefix, whereas + * those starting with nng_ are intended for external consumption. + */ + +/* + * C compilers may get unhappy when named arguments are not used. While + * there are things like __attribute__((unused)) which are arguably + * superior, support for such are not universal. + */ +#define NNI_ARG_UNUSED(x) ((void)x); + +/* + * We have our own snprintf, because some platforms lack this, while + * others need special handling. Ours just calls the vsnprintf version + * from the platform. + */ +extern void nni_snprintf(char *, size_t, const char *, ...); + +/* + * nni_panic is used to terminate the process with prejudice, and + * should only be called in the face of a critical programming error, + * or other situation where it would be unsafe to attempt to continue. + * As this crashes the program, it should never be used when factors outside + * the program can cause it, such as receiving protocol errors, or running + * out of memory. Its better in those cases to return an error to the + * program and let the caller handle the error situation. + */ +extern void nni_panic(const char *, ...); + +#endif /* NNG_IMPL_H */ diff --git a/src/core/panic.c b/src/core/panic.c new file mode 100644 index 00000000..47556f70 --- /dev/null +++ b/src/core/panic.c @@ -0,0 +1,86 @@ +/* + * Copyright 2016 Garrett D'Amore <garrett@damore.org> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> + +#ifdef NNG_HAVE_BACKTRACE +#include <execinfo.h> +#endif + +#include "nng.h" + +#include "nng_impl.h" + +/* + * Panic handling. + */ + +static void +show_backtrace(void) +{ +#if NNG_HAVE_BACKTRACE + void *frames[50]; + int nframes; + char *lines; + int i; + + nframes = backtrace(frames, sizeof (frames) / sizeof (frames[0])); + if (nframes > 1) { + lines = backtrace_symbols(frames, nframes -1); + if (lines == NULL) { + return; + } + for (i = 0; i < nframes; i++) { + nni_debug_out(lines[i]); + } + } +#endif +} + +/* + * nni_panic shows a panic message, a possible stack bracktrace, then aborts + * the process/program. This should only be called when a condition arises + * that should not be possible, e.g. a programming assertion failure. It should + * not be called in situations such as ENOMEM, as nni_panic is fairly rude + * to any application it may be called from within. + */ +void +nni_panic(const char *fmt, ...) +{ + char buf[128]; + char fbuf[128]; + va_list va; + + va_start(va, fmt); + (void) nni_snprintf(fbuf, sizeof (buf), "panic: %s", fmt); + (void) nni_vsnprintf(buf, sizeof (buf), fbuf, va); + va_end(va); + + nni_debug_out(buf); + nni_debug_out("This message is indicative of a BUG."); + nni_debug_out("Report this at http://github.com/nanomsg/nanomsg"); + + show_backtrace(); + nni_abort(); +} diff --git a/src/core/platform.c b/src/core/platform.c new file mode 100644 index 00000000..d96ebe70 --- /dev/null +++ b/src/core/platform.c @@ -0,0 +1,33 @@ +/* + * Copyright 2016 Garrett D'Amore <garrett@damore.org> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* + * This file pulls in the correct platform implementation. + */ + +#include "core/nng_impl.h" + +#if defined(PLATFORM_POSIX) +#include "platform/posix/posix_impl.h" +#else +#error "unknown platform" +#endif diff --git a/src/core/snprintf.c b/src/core/snprintf.c new file mode 100644 index 00000000..3ca73e00 --- /dev/null +++ b/src/core/snprintf.c @@ -0,0 +1,36 @@ +/* + * Copyright 2016 Garrett D'Amore <garrett@damore.org> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include <stdarg.h> +#include <stdint.h> +#include <stdlib.h> + +#include "nng_impl.h" + +void +nni_snprintf(char *dst, size_t sz, const char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + nni_vsnprintf(dst, sz, fmt, va); + va_end(va); +} diff --git a/src/nng.h b/src/nng.h new file mode 100644 index 00000000..1075b95a --- /dev/null +++ b/src/nng.h @@ -0,0 +1,426 @@ +/* + * Copyright 2016 Garrett D'Amore <garrett@damore.org> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef NNG_H +#define NNG_H + +/* + * NNG (nanomsg-ng) is a next generation implementation of the SP protocols. + * The APIs have changed, and there is no attempt to provide API compatibility + * with legacy libnanomsg. This file defines the library consumer-facing + * Public API. Use of definitions or declarations not found in this header file + * is specfically unsupported and strongly discouraged. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <errno.h> +#include <stddef.h> +#include <stdint.h> + +/* + * NNG_DECL is used on declarations to deal with scope. + * For building Windows DLLs, it should be the appropriate + * __declspec(). (We recommend *not* building this library + * as a DLL, but instead linking it statically for your projects + * to minimize questions about link dependencies later.) + */ +#ifndef NNG_DECL +#define NNG_DECL extern +#endif + +/* + * Types common to nng. + */ +typedef struct nng_socket *nng_socket_t; +typedef struct nng_endpt *nng_endpt_t; +typedef struct nn_pipe *nng_pipe_t; +typedef struct nn_msg *nng_msg_t; +typedef struct nn_event *nng_event_t; +typedef struct nng_notify *nng_notify_t; +typedef struct nng_snapshot *nng_snapshot_t; +typedef struct nng_stat *nng_stat_t; + +/* + * nng_socket simply creates a socket of the given class. It returns an + * error code on failure, or zero on success. The socket starts in cooked + * mode. + */ +NNG_DECL int nng_socket_create(nng_socket_t *, int proto); + +/* + * nng_socket_close closes the socket, terminating all activity and + * closing any underlying connections and releasing any associated + * resources. Memory associated with the socket is freed, so it is an + * error to reference the socket in any way after this is called. Likewise, + * it is an error to reference any resources such as end points associated + * with the socket. + */ +NNG_DECL int nng_socket_close(nng_socket_t); + +/* + * nng_socket_setopt sets an option for a specific socket. + */ +NNG_DECL int nng_socket_setopt(nng_socket_t, int, void *, size_t); + +/* + * nng_socket_getopt obtains the option for a socket. + */ +NNG_DECL int nng_socket_getopt(nng_socket_t, int, void *, size_t *); + +/* + * nng_notify_register sets a notification callback. The callback will be + * called for any of the requested events. The callback can be deregistered + * by calling nng_notify_unregister with the same handle. These notification + * callbacks are executed on a separate thread, to avoid potential lock + * recursion. + */ +NNG_DECL nng_notify_t nng_notify_register(nng_socket_t, int, + void (*)(nng_socket_t, nng_event_t, void *), void *); +NNG_DECL int nng_notify_unregister(nng_socket_t, nng_notify_t); + +/* + * Event types. Sockets can have multiple different kind of events. + * Note that these are edge triggered -- therefore the status indicated + * may have changed since the notification occurred. + * + * NNG_EVENT_RECV - A message is ready for receive. + * NNG_EVENT_SEND - A message can be sent. + * NNG_EVENT_ERROR - An error condition on the socket occurred. + * NNG_EVENT_PIPE_ADD - A new pipe (connection) is added to the socket. + * The argument is an nn_pipe_t. + * NNG_EVENT_PIPE_RM - A pipe (connection) is removed from the socket. + * The argument is an nn_pipe_t. + * NNG_EVENT_ENDPT_ADD - An endpoint is added to the socket. + * The argument is an nn_endpt_t. + * NNG_EVENT_ENDPT_RM - An endpoint is removed from the socket. + * The argument is an nn_endpt_t. + */ +#define NNG_EVENT_BIT(x) (1U << (x)) +#define NNG_EVENT_RECV NNG_EVENT_BIT(0) +#define NNG_EVENT_SEND NNG_EVENT_BIT(1) +#define NNG_EVENT_ERROR NNG_EVENT_BIT(2) +#define NNG_EVENT_PIPE_ADD NNG_EVENT_BIT(3) +#define NNG_EVENT_PIPE_RM NNG_EVENT_BIT(4) +#define NNG_EVENT_ENDPT_ADD NNG_EVENT_BIT(5) +#define NNG_EVENT_ENDPT_RM NNG_EVENT_BIT(6) + +/* + * The following functions return more detailed information about the event. + * Some of the values will not make sense for some event types, in which case + * the value returned will be NULL. + */ +NNG_DECL int nng_event_type(nng_event_t); +NNG_DECL nng_socket_t nng_event_socket(nng_event_t); +NNG_DECL nng_endpt_t nng_event_endpt(nng_event_t); +NNG_DECL nng_pipe_t nng_event_pipe(nng_event_t); +NNG_DECL const char *nng_event_reason(nng_event_t); + +/* + * nng_socket_listen creates a listening endpoint with no special options, + * and starts it listening. It is functionally equivalent to the legacy + * nn_bind(). The underlying endpoint is returned back to the caller. + */ +NNG_DECL int nng_socket_listen(nng_endpt_t *, nng_socket_t, const char *); + +/* + * nng_socket_dial creates a dialing endpoint, with no special options, + * and starts it dialing. Dialers have at most one active connection at a + * time. This is similar to the legacy nn_connect(). The underlying endpoint + * is returned back to the caller. + */ +NNG_DECL int nng_socket_dial(nng_endpt_t *, nng_socket_t, const char *); + +/* + * nng_socket_endpt creates an endpoint on the socket, but does not + * start it either dialing or connecting. + */ +NNG_DECL int nng_socket_endpt(nng_endpt_t *, nng_socket_t, const char *); + +/* + * nng_endpt_dial starts the endpoint dialing. This is only possible if + * the endpoint is not already dialing or listening. + */ +NNG_DECL int nng_endpt_dial(nng_endpt_t); + +/* + * nng_endpt_listen starts the endpoint listening. This is only possible if + * the endpoint is not already dialing or listening. + */ +NNG_DECL int nng_endpt_listen(nng_endpt_t); + +/* + * nng_endpt_close closes the endpt, shutting down all underlying + * connections and releasing all associated resources. It is an error to + * refer to the endpoint after this is called. + */ +NNG_DECL int nng_endpt_close(nng_endpt_t); + +/* + * nng_endpt_setopt sets an option for a specific endpoint. Note + * endpoint options may not be altered on a running endpoint. + */ +NNG_DECL int nng_endpt_setopt(nng_endpt_t, int, void *, size_t); + +/* + * nng_endpt_getopt obtains the option for an endpoint. + */ +NNG_DECL int nng_endpt_getopt(nng_endpt_t, int, void *, size_t *); + +/* + * nng_strerror returns a human readable string associated with the error + * code supplied. + */ +NNG_DECL const char *nng_strerror(int); + +/* + * nng_send sends (or arranges to send) the data on the socket. Note that + * this function may (will!) return before any receiver has actually + * received the data. The return value will be zero to indicate that the + * socket has accepted the entire data for send, or an errno to indicate + * failure. The flags may include NNG_FLAG_NONBLOCK. + */ +NNG_DECL int nng_send(nng_socket_t, const void *, size_t, int); + +/* + * nng_recv receives message data into the socket, up to the supplied size. + * The actual size of the message data will be written to the value pointed + * to by size. The flags may include NNG_FLAG_NONBLOCK and NNG_FLAG_ALLOC. + * If NNG_FLAG_ALLOC is supplied then the library will allocate memory for + * the caller. In that case the pointer to the allocated will be stored + * instead of the data itself. The caller is responsible for freeing the + * associated memory with free(). + */ +NNG_DECL int nng_recv(nng_socket_t, void *, size_t *, int); + +/* + * nng_sendmsg is like nng_send, but offers up a message structure, which + * gives the ability to provide more control over the message, including + * providing backtrace information. It also can take a message that was + * obtain via nn_recvmsg, allowing for zero copy forwarding. + */ +NNG_DECL int nng_sendmsg(nng_socket_t, nng_msg_t, int); + +/* + * nng_recvmsg is like nng_recv, but is used to obtain a message structure + * as well as the data buffer. This can be used to obtain more information + * about where the message came from, access raw headers, etc. It also + * can be passed off directly to nng_sendmsg. + */ +NNG_DECL int nng_recvmsg(nng_socket_t, nng_msg_t *, int); + +/* + * Message API. + */ +NNG_DECL int nng_msg_alloc(nng_msg_t *, size_t); +NNG_DECL void nng_msg_free(nng_msg_t); +NNG_DECL const char *nng_msg_data(nng_msg_t); +NNG_DECL int nng_msg_realloc(nng_msg_t, size_t); +NNG_DECL void *nng_msg_header(nng_msg_t, size_t *); +NNG_DECL void *nng_msg_body(nng_msg_t, size_t *); +NNG_DECL int nng_msg_port(nng_msg_t, nng_pipe_t *); + +/* + * Pipe API. Generally pipes are only "observable" to applications, but + * we do permit an application to close a pipe. This can be useful, for + * example during a connection notification, to disconnect a pipe that + * is associated with an invalid or untrusted remote peer. + */ +NNG_DECL int nng_pipe_getopt(nng_pipe_t, int, void *, size_t *); +NNG_DECL int nng_pipe_close(nng_pipe_t); + +/* + * Protocol numbers. These are to be used with nng_socket_create(). + * These values are used on the wire, so must not be changed. The major + * number of the protocol is shifted left by 4 bits, and a subprotocol is + * assigned in the lower 4 bits. + * + * There are gaps in the list, which are obsolete or unsupported protocols. + * For now we assume that protocol numbers are never more than 16 bits. + */ +#define NNG_PROTO(major, minor) (((major) * 16) + (minor)) +#define NNG_PROTO_PAIR NNG_PROTO(1, 0) +#define NNG_PROTO_PUB NNG_PROTO(2, 0) +#define NNG_PROTO_SUB NNG_PROTO(2, 1) +#define NNG_PROTO_REQ NNG_PROTO(3, 0) +#define NNG_PROTO_REP NNG_PROTO(3, 1) +#define NNG_PROTO_PUSH NNG_PROTO(5, 0) +#define NNG_PROTO_PULL NNG_PROTO(5, 1) +#define NNG_PROTO_SURVEYOR NNG_PROTO(6, 2) +#define NNG_PROTO_RESPONDENT NNG_PROTO(6, 3) +#define NNG_PROTO_BUS NNG_PROTO(7, 0) +#define NNG_PROTO_STAR NNG_PROTO(100, 0) + +/* + * Options. We encode option numbers as follows: + * + * <level> - 0: socket, 1: transport + * <type> - zero (socket), or transport (8 bits) + * <code> - specific value (16 bits) + * + */ +#define NNG_OPT_SOCKET(c) (c) +#define NNG_OPT_TRANSPORT_OPT(t, c) (0x10000 | ((p) << 16) | (c)) + +#define NNG_OPT_RAW NNG_OPT_SOCKET(0) +#define NNG_OPT_LINGER NNG_OPT_SOCKET(1) +#define NNG_OPT_RCVBUF NNG_OPT_SOCKET(2) +#define NNG_OPT_SNDBUF NNG_OPT_SOCKET(3) +#define NNG_OPT_RCVTIMEO NNG_OPT_SOCKET(4) +#define NNG_OPT_SNDTIMEO NNG_OPT_SOCKET(5) +#define NNG_OPT_RECONN_TIME NNG_OPT_SOCKET(6) +#define NNG_OPT_RECONN_MAXTIME NNG_OPT_SOCKET(7) +#define NNG_OPT_RCVMAXSZ NNG_OPT_SOCKET(8) +#define NNG_OPT_MAXTTL NNG_OPT_SOCKET(9) +#define NNG_OPT_PROTOCOL NNG_OPT_SOCKET(10) +#define NNG_OPT_SUBSCRIBE NNG_OPT_SOCKET(11) +#define NNG_OPT_UNSUBSCRIBE NNG_OPT_SOCKET(12) +#define NNG_OPT_SURVEYTIME NNG_OPT_SOCKET(13) +#define NNG_OPT_RESENDTIME NNG_OPT_SOCKET(14) +#define NNG_OPT_TRANSPORT NNG_OPT_SOCKET(15) +#define NNG_OPT_LOCALADDR NNG_OPT_SOCKET(16) +#define NNG_OPT_REMOTEADDR NNG_OPT_SOCKET(17) +#define NNG_OPT_RECVFD NNG_OPT_SOCKET(18) +#define NNG_OPT_SENDFD NNG_OPT_SOCKET(19) + +/* XXX: TBD: priorities, socket names, ipv4only */ + +/* + * Statistics. These are for informational purposes only, and subject + * to change without notice. The API for accessing these is stable, + * but the individual statistic names, values, and meanings are all + * subject to change. + */ + +/* + * nng_snapshot_create creates a statistics snapshot. The snapshot + * object must be deallocated expressly by the user, and may persist beyond + * the lifetime of any socket object used to update it. Note that the + * values of the statistics are initially unset. + */ +NNG_DECL int nng_snapshot_create(nng_snapshot_t *); + +/* + * nng_snapshot_free frees a snapshot object. All statistic objects + * contained therein are destroyed as well. + */ +NNG_DECL void nng_snapshot_free(nng_snapshot_t); + +/* + * nng_snapshot_update updates a snapshot of all the statistics + * relevant to a particular socket. All prior values are overwritten. + * It is acceptable to use the same snapshot object with different + * sockets. + */ +NNG_DECL int nng_snapshot_update(nng_socket_t, nng_snapshot_t); + +/* + * nng_snapshot_iterate is used to iterate over the individual statistic + * objects inside the snapshot. Note that the statistic object, and the + * meta-data for the object (name, type, units) is fixed, and does not + * change for the entire life of the snapshot. Only the value + * is subject to change, and then only when a snapshot is updated. + * + * Iteration begins by providing NULL in the value referenced. Successive + * calls will update this value, returning NULL when no more statistics + * are available in the snapshot. + */ +NNG_DECL nng_stat_t nng_snapshot_iterate(nng_snapshot_t, nng_stat_t); + +/* + * nng_stat_name is used to determine the name of the statistic. + * This is a human readable name. Statistic names, as well as the presence + * or absence or semantic of any particular statistic are not part of any + * stable API, and may be changed without notice in future updates. + */ +NNG_DECL const char *nng_stat_name(nng_stat_t); + +/* + * nng_stat_type is used to determine the type of the statistic. + * At present, only NNG_STAT_TYPE_LEVEL and and NNG_STAT_TYPE_COUNTER + * are defined. Counters generally increment, and therefore changes in the + * value over time are likely more interesting than the actual level. Level + * values reflect some absolute state however, and should be presented to the + * user as is. + */ +NNG_DECL int nng_stat_type(nng_stat_t); +#define NNG_STAT_LEVEL 0 +#define NNG_STAT_COUNTER 1 + +/* + * nng_stat_unit provides information about the unit for the statistic, + * such as NNG_UNIT_BYTES or NNG_UNIT_BYTES. If no specific unit is + * applicable, such as a relative priority, then NN_UNIT_NONE is + * returned. + */ +NNG_DECL int nng_stat_unit(nng_stat_t); +#define NNG_UNIT_NONE 0 +#define NNG_UNIT_BYTES 1 +#define NNG_UNIT_MESSAGES 2 +#define NNG_UNIT_BOOLEAN 3 +#define NNG_UNIT_MILLIS 4 +#define NNG_UNIT_EVENTS 5 + +/* + * nng_stat_value returns returns the actual value of the statistic. + * Statistic values reflect their value at the time that the corresponding + * snapshot was updated, and are undefined until an update is performed. + */ +NNG_DECL int64_t nng_stat_value(nng_stat_t); + +/* + * Device functionality. This connects two sockets together in a device, + * which means that messages from one side are forwarded to the other. + */ +NNG_DECL int nng_device(nng_socket_t, nng_socket_t); + +/* + * Pollset functionality. TBD. (Note that I'd rather avoid this + * altogether, because I believe that the notification mechanism I've + * created offers a superior way to handle this. I don't think many + * direct consumers of nn_poll existed in the wild, except via nn_device(). + * I suspect that there not even many nn_device() consumers.) + */ + +/* + * Symbol name and visibility. TBD. The only symbols that really should + * be directly exported to runtimes IMO are the option symbols. And frankly + * they have enough special logic around them that it might be best not to + * automate the promotion of them to other APIs. This is an area open + * for discussion. + */ + +/* + * Error codes. These may happen to align to errnos used on your platform, + * but do not count on this. + */ +#define NNG_ENOMEM (-2) +#define NNG_EINVAL (-3) +#define NNG_EBUSY (-4) + +#ifdef __cplusplus +} +#endif + +#endif /* NNG_H */ diff --git a/src/platform/platform.h b/src/platform/platform.h new file mode 100644 index 00000000..05f55be9 --- /dev/null +++ b/src/platform/platform.h @@ -0,0 +1,125 @@ +/* + * Copyright 2016 Garrett D'Amore <garrett@damore.org> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef PLATFORM_H +#define PLATFORM_H + +#include <stdlib.h> +#include <stdint.h> +#include <stdarg.h> + +/* + * These are the APIs that a platform must implement to support nng. + */ + +/* + * nni_abort crashes the system; it should do whatever is appropriate + * for abnormal programs on the platform, such as calling abort(). + */ +void nni_abort(void); + +/* + * nni_vnsprintf is exactly like its POSIX counterpart. + * Some platforms (Windows!) need a special version of this. + */ +void nni_vsnprintf(char *, size_t, const char *, va_list); + +/* + * nni_debug_output is used to emit debug messages. Typically this is used + * during core debugging, or to emit panic messages. Message content will + * not contain newlines, but the output will add them. + */ +void nni_debug_out(const char *); + +/* + * nni_set_debug_output is used to redirect debug output; for example an + * application could replace the default output routine with one that sends + * it's output to syslog. If NULL is specified, then a default handler + * used instead. The handler should add any newlines to the output as + * required. The default handler writes to standard error. + */ +void nni_set_debug_out(void (*)(const char *)); + +/* + * nni_alloc allocates memory. In most cases this can just be malloc(). + * However, you may provide a different allocator, for example it is + * possible to use a slab allocator or somesuch. It is permissible for this + * to return NULL if memory cannot be allocated. + */ +void *nni_alloc(size_t); + +/* + * nni_free frees memory allocated with nni_alloc. It takes a size because + * some allocators do not track size, or can operate more efficiently if + * the size is provided with the free call. Examples of this are slab + * allocators like this found in Solaris/illumos (see libumem or kmem). + * This routine does nothing if supplied with a NULL pointer and zero size. + * Most implementations can just call free() here. + */ +void nni_free(void *, size_t); + +typedef struct nni_mutex *nni_mutex_t; +typedef struct nni_cond *nni_cond_t; + +/* + * Mutex handling. + */ +int nni_mutex_create(nni_mutex_t *); +void nni_mutex_destroy(nni_mutex_t); +void nni_mutex_enter(nni_mutex_t); +void nni_mutex_exit(nni_mutex_t); +int nni_mutex_tryenter(nni_mutex_t); +int nni_cond_create(nni_cond_t *, nni_mutex_t); +void nni_cond_destroy(nni_cond_t); + +/* + * nni_cond_broadcast wakes all waiters on the condition. This should be + * called with the lock held. + */ +int nni_cond_broadcast(nni_cond_t); + +/* + * nni_cond_signal wakes a signal waiter. + */ +int nni_cond_signal(nni_cond_t); + +/* + * nni_condwait waits for a wake up on the condition variable. The + * associated lock is atomically released and reacquired upon wake up. + * Callers can be spuriously woken. The return value is 0 on success, + * or an error code. (Most implementations should never return an error.) + * Most callers will ignore the return value. The associated lock must + * be held. + */ +int nni_cond_wait(nni_cond_t); + +/* + * nni_cond_timedwait waits for a wakeup on the condition variable, just + * as with nni_condwait, but it will also wake after the given number of + * milliseconds has passed. (This is a relative timed wait.) Early + * wakeups are permitted, and the caller must take care to double check any + * conditions. The return value is 0 on success, or an error code, which + * can be NNG_ETIMEDOUT. + */ +int nnp_cond_timedwait(nni_cond_t, int); + +#endif /* PLATFORM_H */ diff --git a/src/platform/posix/posix_alloc.h b/src/platform/posix/posix_alloc.h new file mode 100644 index 00000000..8451cc9c --- /dev/null +++ b/src/platform/posix/posix_alloc.h @@ -0,0 +1,45 @@ +/* + * Copyright 2016 Garrett D'Amore <garrett@damore.org> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* + * This is more of a direct #include of a .c rather than .h file. + * But having it be a .h makes compiler rules work out properly. Do + * not include this more than once into your program, or you will + * get multiple symbols defined. + */ + +/* + * POSIX memory allocation. This is pretty much standard C. + */ + +void * +nni_alloc(size_t size) +{ + return (malloc(size)); +} + +void +nni_free(void *ptr, size_t size) +{ + NNI_ARG_UNUSED(size); + free(ptr); +} diff --git a/src/platform/posix/posix_debug.h b/src/platform/posix/posix_debug.h new file mode 100644 index 00000000..d25a6c11 --- /dev/null +++ b/src/platform/posix/posix_debug.h @@ -0,0 +1,48 @@ +/* + * Copyright 2016 Garrett D'Amore <garrett@damore.org> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include <stdlib.h> +#include <stdio.h> + +void (*debug_out)(const char *line); + +void +nni_abort(void) +{ + abort(); +} + +void +nni_debug_out(const char *message) +{ + if (debug_out != NULL) { + debug_out(message); + } else { + (void) fprintf(stderr, "%s\n", message); + } +} + +void +nni_set_debug_out(void (*out)(const char *)) +{ + debug_out = out; +} diff --git a/src/platform/posix/posix_impl.h b/src/platform/posix/posix_impl.h new file mode 100644 index 00000000..291e3f2e --- /dev/null +++ b/src/platform/posix/posix_impl.h @@ -0,0 +1,41 @@ +/* + * Copyright 2016 Garrett D'Amore <garrett@damore.org> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* + * This is more of a direct #include of a .c rather than .h file. + * But having it be a .h makes compiler rules work out properly. Do + * not include this more than once into your program, or you will + * get multiple symbols defined. + * + * The file itself pulls in POSIX implementations for platform specific + * functionality. + */ + +#ifdef PLATFORM_POSIX + +#include "platform/posix/posix_debug.h" +#include "platform/posix/posix_alloc.h" +#include "platform/posix/posix_synch.h" +/* #include "platform/posix/posix_thread.h" */ +#include "platform/posix/posix_vsnprintf.h" + +#endif diff --git a/src/platform/posix/posix_synch.h b/src/platform/posix/posix_synch.h new file mode 100644 index 00000000..4a3a152d --- /dev/null +++ b/src/platform/posix/posix_synch.h @@ -0,0 +1,143 @@ +/* + * Copyright 2016 Garrett D'Amore <garrett@damore.org> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* + * This is more of a direct #include of a .c rather than .h file. + * But having it be a .h makes compiler rules work out properly. Do + * not include this more than once into your program, or you will + * get multiple symbols defined. + */ + +/* + * POSIX synchronization (mutexes and condition variables). + */ + +#include <pthread.h> + +struct nni_mutex { + pthread_mutex_t mx; +}; + +struct nni_cond { + pthread_cond_t cv; + pthread_mutex_t *mx; +}; + +int +nni_mutex_create(nni_mutex_t *mp) +{ + struct nni_mutex *m; + pthread_mutexattr_t attr; + int rv; + + if ((m = nni_alloc(sizeof (*m))) == NULL) { + return (NNG_ENOMEM); + } + + /* We ask for more error checking... */ + if (pthread_mutexattr_init(&attr) != 0) { + nni_free(m, sizeof (*m)); + return (NNG_ENOMEM); + } + + if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK) != 0) { + nni_panic("pthread_mutexattr_settype failed"); + } + + rv = pthread_mutex_init(&m->mx, &attr); + + if (pthread_mutexattr_destroy(&attr) != 0) { + nni_panic("pthread_mutexattr_destroy failed"); + } + + if (rv != 0) { + nni_free(m, sizeof (*m)); + return (NNG_ENOMEM); + } + *mp = m; + return (0); +} + +void +nni_mutex_destroy(nni_mutex_t m) +{ + if (pthread_mutex_destroy(&m->mx) != 0) { + nni_panic("pthread_mutex_destroy failed"); + } + /* + * If destroy fails for some reason, we can't really do + * anything about it. This would actually represent a programming + * bug, and the right thing to do here would be to panic. + */ + nni_free(m, sizeof (*m)); +} + +void +nni_mutex_enter(nni_mutex_t m) +{ + if (pthread_mutex_lock(&m->mx) != 0) { + nni_panic("pthread_mutex_lock failed"); + } +} + +void +nni_mutex_exit(nni_mutex_t m) +{ + if (pthread_mutex_unlock(&m->mx) != 0) { + nni_panic("pthread_mutex_unlock failed"); + } +} + +int +nni_mutex_tryenter(nni_mutex_t m) +{ + if (pthread_mutex_trylock(&m->mx) != 0) { + return (NNG_EBUSY); + } + return (0); +} + +int +nni_cond_create(nni_cond_t *cvp, nni_mutex_t mx) +{ + struct nni_cond *c; + if ((c = nni_alloc(sizeof (*c))) == NULL) { + return (NNG_ENOMEM); + } + c->mx = &mx->mx; + if (pthread_cond_init(&c->cv, NULL) != 0) { + /* In theory could be EAGAIN, but handle like ENOMEM */ + nni_free(c, sizeof (*c)); + return (NNG_ENOMEM); + } + *cvp = c; + return (0); +} + +void +nni_cond_destroy(nni_cond_t c) +{ + if (pthread_cond_destroy(&c->cv) != 0) { + nni_panic("pthread_cond_destroy failed"); + } + nni_free(c, sizeof (*c)); +} diff --git a/src/platform/posix/posix_vsnprintf.h b/src/platform/posix/posix_vsnprintf.h new file mode 100644 index 00000000..8270d255 --- /dev/null +++ b/src/platform/posix/posix_vsnprintf.h @@ -0,0 +1,30 @@ +/* + * Copyright 2016 Garrett D'Amore <garrett@damore.org> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include <stdarg.h> +#include <stdio.h> + +void +nni_vsnprintf(char *dst, size_t sz, const char *fmt, va_list va) +{ + (void) vsnprintf(dst, sz, fmt, va); +} |
