aboutsummaryrefslogtreecommitdiff
path: root/src/core/platform.h
blob: a674aac535c5288769828f5b15a9627dbf895626 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
//
// Copyright 2016 Garrett D'Amore <garrett@damore.org>
//
// This software is supplied under the terms of the MIT License, a
// copy of which should be located in the distribution where this
// file was obtained (LICENSE.txt).  A copy of the license may also be
// found online at https://opensource.org/licenses/MIT.
//

#ifndef CORE_PLATFORM_H
#define CORE_PLATFORM_H

// We require some standard C header files.  The only one of these that might
// be problematic is <stdint.h>, which is required for C99.  Older versions
// of the Windows compilers might not have this.  However, latest versions of
// MS Studio have a functional <stdint.h>.  If this impacts you, just upgrade
// your tool chain.
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>

// These are the APIs that a platform must implement to support nng.

// A word about fork-safety: This library is *NOT* fork safe, in that
// functions may not be called in the child process without an intervening
// exec().  The library attempts to detect this situation, and crashes the
// process with an error message if it encounters it.  (See nn_platform_init
// below.)
//
// Additionally, some file descriptors may leak across fork even to
// child processes.  We make every reasonable effort to ensure that this
// does not occur, but on some platforms there are unavoidable race
// conditions between file creation and marking the file close-on-exec.
//
// Forkers should use posix_spawn() if possible, and as much as possible
// arrange for file close on exec by posix_spawn, or close the descriptors
// they do not need in the child.  (Note that posix_spawn() does *NOT*
// arrange for pthread_atfork() handlers to be called on some platforms.)

// nni_plat_abort crashes the system; it should do whatever is appropriate
// for abnormal programs on the platform, such as calling abort().
extern void nni_plat_abort(void);

// nni_plat_println 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.
extern void nni_plat_println(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.
extern 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.
extern void nni_free(void *, size_t);

typedef struct nni_mutex	nni_mutex;
typedef struct nni_cond		nni_cond;

// Mutex handling.

// nni_mutex_init initializes a mutex structure.  This may require dynamic
// allocation, depending on the platform.  It can return NNG_ENOMEM if that
// fails.
extern int nni_mutex_init(nni_mutex *);

// nni_mutex_fini destroys the mutex and releases any resources allocated for
// it's use.
extern void nni_mutex_fini(nni_mutex *);

// nni_mutex_enter locks the mutex.  This is not recursive -- a mutex can only
// be entered once.
extern void nni_mutex_enter(nni_mutex *);

// nni_mutex_exit unlocks the mutex.  This can only be performed by the thread
// that owned the mutex.
extern void nni_mutex_exit(nni_mutex *);

// nni_mutex_tryenter tries to lock the mutex.  If it can't, it may return
// NNG_EBUSY.
extern int nni_mutex_tryenter(nni_mutex *);

// nni_cond_init initializes a condition variable.  We require a mutex be
// supplied with it, and that mutex must always be held when performing any
// operations on the condition variable (other than fini.)  This may require
// dynamic allocation, and if so this operation may fail with NNG_ENOMEM.
extern int nni_cond_init(nni_cond *, nni_mutex *);

// nni_cond_fini releases all resources associated with condition variable.
extern void nni_cond_fini(nni_cond *);

// nni_cond_broadcast wakes all waiters on the condition.  This should be
// called with the lock held.
extern void nni_cond_broadcast(nni_cond *);

// nni_cond_signal wakes a signal waiter.
extern void nni_cond_signal(nni_cond *);

// nni_cond_wait 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 associated lock must be held.
extern void nni_cond_wait(nni_cond *);

// nni_cond_waituntil waits for a wakeup on the condition variable, or
// until the system time reaches the specified absolute time.  (It is an
// absolute form of nni_cond_timedwait.)  Early wakeups are possible, so
// check the condition.  It will return either NNG_ETIMEDOUT, or 0.
extern int nni_cond_waituntil(nni_cond *, nni_time);

typedef struct nni_thread nni_thread;

// nni_thread_creates a thread that runs the given function. The thread
// receives a single argument.
extern int nni_thread_create(nni_thread **, void (*fn)(void *), void *);

// nni_thread_reap waits for the thread to exit, and then releases any
// resources associated with the thread.  After this returns, it
// is an error to reference the thread in any further way.
extern void nni_thread_reap(nni_thread *);

// nn_clock returns a number of microseconds since some arbitrary time
// in the past.  The values returned by nni_clock must use the same base
// as the times used in nni_cond_waituntil.  The nni_clock() must return
// values > 0, and must return values smaller than 2^63.  (We could relax
// this last constraint, but there is no reason to, and leaves us the option
// of using negative values for other purposes in the future.)
extern nni_time nni_clock(void);

// nni_usleep sleeps for the specified number of microseconds (at least).
extern void nni_usleep(nni_duration);

// nni_platform_init is called to allow the platform the chance to
// do any necessary initialization.  This routine MUST be idempotent,
// and threadsafe, and will be called before any other API calls, and
// may be called at any point thereafter.  It is permitted to return
// an error if some critical failure inializing the platform occurs,
// but once this succeeds, all future calls must succeed as well, unless
// nni_plat_fini has been called.
//
// The function argument should be called if the platform has not initialized
// (i.e. exactly once), and its result passed back to the caller.  If it
// does not return 0 (success), then it may be called again to try to
// initialize the platform again at a later date.
extern int nni_plat_init(int (*)(void));

// nni_platform_fini is called to clean up resources.  It is intended to
// be called as the last thing executed in the library, and no other functions
// will be called until nni_platform_init is called.
extern void nni_plat_fini(void);

// Actual platforms we support.  This is included up front so that we can
// get the specific types that are supplied by the platform.
#if defined(PLATFORM_POSIX)
#include "platform/posix/posix_impl.h"
#else
#error "unknown platform"
#endif

#endif // CORE_PLATFORM_H