Serene Runtime 1.0.0-dev
C runtime for the Serene programming language
Loading...
Searching...
No Matches
errors.h File Reference

Error handling for the runtime. More...

#include <stddef.h>
#include <stdint.h>
Include dependency graph for errors.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  srn_trace_frames_t
 This is how we create a traceback for the C level code in the runtime lib. More...
struct  srn_error_t
 A runtime error, a tag classifying the failure and a human-readable message. More...

Macros

#define ERROR_HEADER   srn_error_t *maybe_error
 The first member every fallible data structure embeds (see the file note above).
#define TRACE_FRAME_DEPTH   32
#define HAS_ERROR(x)
 True when x – a value carrying an ERROR_HEADER – has an error attached.
#define PUSH_TRACE(ctx, x)
#define PUSH_AND_MOVE(ctx, from, to)
#define ERR(ctx, err, msg)
#define ERR_FROM(ctx, src)
#define FAIL(ctx, T, err, msg)

Typedefs

typedef enum srn_error_tag_t srn_error_tag_t
 The kind of a failure.
typedef enum srn_error_layer_t srn_error_layer_t
typedef struct srn_trace_frames_t srn_trace_frames_t
 This is how we create a traceback for the C level code in the runtime lib.
typedef struct srn_error_t srn_error_t
 A runtime error, a tag classifying the failure and a human-readable message.

Enumerations

enum  srn_error_tag_t {
  ABSURD , NOT_IMPLEMENTED , SEQ_LIMIT_REACHED , AL_LIMIT_REACHED ,
  SM_BUFFER_ID_EXHAUSTED , INDEX_OUT_OF_BOUND , CORRUPTED_SEQ , CORRUPTED_ARRAY_LIST ,
  FAILED_TO_ADD_MODULE , JIT_IR_PARSE_FAILED , JIT_SYMBOL_NOT_FOUND , JIT_SYMBOL_ALREADY_DEFINED ,
  EMPTY_NAMESPACE_NAME , EMPTY_SYMBOL_NAME , STRING_LENGTH_LIMIT_EXCEEDED , WRONG_RANGE ,
  EPOLL_INIT_FAILED , EPOLL_EVENT_FD_FAILED , EPOLL_FAILED_TO_ADD_FD , CONNECTION_REFUSED ,
  CONNECTION_RESET , CONNECTION_ABORTED , BROKEN_PIPE , BAD_DESCRIPTOR ,
  TIMED_OUT , CANCELED , NOT_CONNECTED , ADDRESS_IN_USE ,
  HOST_UNREACHABLE , NETWORK_UNREACHABLE , MESSAGE_TOO_LONG , TOO_MANY_OPEN_FILES ,
  PERMISSION_DENIED , OUT_OF_MEMORY , NOT_FOUND , ALREADY_EXISTS ,
  IS_A_DIRECTORY , NOT_A_DIRECTORY , NO_SPACE , INVALID_ARGUMENT ,
  CHANNEL_BUSY , MEMORY_MAP_FAILED , IR_PARSE_ERROR , UNKNOWN_IO_ERROR
}
 The kind of a failure. More...
enum  srn_error_layer_t : uint8_t { SRN_ERR_RUNTIME_LAYER = 0 }

Functions

void srn_error_trace_push (const srn_context_t *ctx, srn_error_t *err, void *addr)
 Append a return address to the trace of a runtime-layer error.
srn_error_t * srn_errors_make (const srn_context_t *ctx, srn_error_tag_t tag, const char *msg, void *caller)
 Build an error in ctx's memory, tagged tag and carrying msg.
srn_error_t * srn_errors_from (const srn_context_t *ctx, const srn_error_t *src, void *caller)
 Build an error in ctx's memory that carries the layer, tag, message and payload of src, with a trace of its own that starts at caller.
void * srn_errors_fail (const srn_context_t *ctx, size_t size, size_t alignment, srn_error_tag_t tag, const char *msg, void *caller)
 Allocate a size/alignment value in ctx, set its ERROR_HEADER (the first member, srn_error_t *maybe_error) to a fresh error, and return it.
char * srn_error_to_string (const srn_context_t *ctx, const srn_error_t *err)
 Format err as a newly allocated string of the form [TAG]: msg, allocated in ctx.
srn_error_tag_t srn_errno_to_tag (int errno_value, srn_error_tag_t default_tag)
 Map a POSIX errno to error tag.

Detailed Description

Error handling for the runtime.

An srn_error_t pairs a srn_error_tag_t, naming the kind of failure, with a human-readable message, and is allocated from a srn_context_t. Fallible operations report failures as these explicit error values rather than through return codes or out-of-band state, so an error travels with the data it describes. When the Serene language layer is built, an error can also be lifted into a srn_value_t so the failure is representable inside the language itself.

IMPORTANT NOTE:

By Convention, all the data structures that in anyway may, interacting with them may result in an error, must use the ERROR_HEADER macro as the first member. For example:

typedef struct foo {
bar other;
...
} foo;
#define ERROR_HEADER
The first member every fallible data structure embeds (see the file note above).
Definition errors.h:62

Following this convention let you use the error handling helpers from this header uniformly across the runtime library.

Definition in file errors.h.

Macro Definition Documentation

◆ ERR

#define ERR ( ctx,
err,
msg )
Value:
srn_errors_make(ctx, err, msg, __builtin_return_address(0))
srn_error_t * srn_errors_make(const srn_context_t *ctx, srn_error_tag_t tag, const char *msg, void *caller)
Build an error in ctx's memory, tagged tag and carrying msg.
Definition errors.c:196

Definition at line 223 of file errors.h.

◆ ERR_FROM

#define ERR_FROM ( ctx,
src )
Value:
srn_errors_from(ctx, src, __builtin_return_address(0))
srn_error_t * srn_errors_from(const srn_context_t *ctx, const srn_error_t *src, void *caller)
Build an error in ctx's memory that carries the layer, tag, message and payload of src,...
Definition errors.c:207

Definition at line 224 of file errors.h.

◆ ERROR_HEADER

#define ERROR_HEADER   srn_error_t *maybe_error

The first member every fallible data structure embeds (see the file note above).

Holds the error attached to a value, or null when the value is good.

Definition at line 62 of file errors.h.

◆ FAIL

#define FAIL ( ctx,
T,
err,
msg )
Value:
((T *)srn_errors_fail(ctx, sizeof(T), alignof(T), err, msg, __builtin_return_address(0)))
void * srn_errors_fail(const srn_context_t *ctx, size_t size, size_t alignment, srn_error_tag_t tag, const char *msg, void *caller)
Allocate a size/alignment value in ctx, set its ERROR_HEADER (the first member, srn_error_t *maybe_er...
Definition errors.c:224

Definition at line 225 of file errors.h.

225#define FAIL(ctx, T, err, msg) \
226 ((T *)srn_errors_fail(ctx, sizeof(T), alignof(T), err, msg, __builtin_return_address(0)))

◆ HAS_ERROR

#define HAS_ERROR ( x)
Value:
((x).maybe_error != nullptr)

True when x – a value carrying an ERROR_HEADER – has an error attached.

Takes the struct by value, the way these results are passed and checked.

Definition at line 70 of file errors.h.

◆ PUSH_AND_MOVE

#define PUSH_AND_MOVE ( ctx,
from,
to )
Value:
{ \
(to).maybe_error = (from).maybe_error; \
PUSH_TRACE(ctx, to); \
}

Definition at line 73 of file errors.h.

73#define PUSH_AND_MOVE(ctx, from, to) \
74 { \
75 (to).maybe_error = (from).maybe_error; \
76 PUSH_TRACE(ctx, to); \
77 }

◆ PUSH_TRACE

#define PUSH_TRACE ( ctx,
x )
Value:
srn_error_trace_push(ctx, (x).maybe_error, __builtin_return_address(0))
void srn_error_trace_push(const srn_context_t *ctx, srn_error_t *err, void *addr)
Append a return address to the trace of a runtime-layer error.
Definition errors.c:177

Definition at line 72 of file errors.h.

◆ TRACE_FRAME_DEPTH

#define TRACE_FRAME_DEPTH   32

Definition at line 63 of file errors.h.

Typedef Documentation

◆ srn_error_layer_t

◆ srn_error_t

typedef struct srn_error_t srn_error_t

A runtime error, a tag classifying the failure and a human-readable message.

◆ srn_error_tag_t

The kind of a failure.

Each srn_error_t carries one of these.

◆ srn_trace_frames_t

typedef struct srn_trace_frames_t srn_trace_frames_t

This is how we create a traceback for the C level code in the runtime lib.

It's similar to how Zig handles it as well. The hard part is to remember to call __builtin_return_address(0) every time we return an error and add the address to this array. Later we can use dladdr on POSIX (and SymFromAddr on Windows) to resolve the function name, and look up the line number in DWARF data when printing the error.

Enumeration Type Documentation

◆ srn_error_layer_t

enum srn_error_layer_t : uint8_t
Enumerator
SRN_ERR_RUNTIME_LAYER 

Definition at line 150 of file errors.h.

150 : uint8_t {
152#ifdef SRN_WITH_SERENE
153 SRN_ERR_PARSE_LAYER,
154#endif
srn_error_layer_t
Definition errors.h:150
@ SRN_ERR_RUNTIME_LAYER
Definition errors.h:151

◆ srn_error_tag_t

The kind of a failure.

Each srn_error_t carries one of these.

Enumerator
ABSURD 
NOT_IMPLEMENTED 
SEQ_LIMIT_REACHED 
AL_LIMIT_REACHED 
SM_BUFFER_ID_EXHAUSTED 
INDEX_OUT_OF_BOUND 
CORRUPTED_SEQ 
CORRUPTED_ARRAY_LIST 
FAILED_TO_ADD_MODULE 
JIT_IR_PARSE_FAILED 

The JIT could not parse a module's LLVM IR or bitcode.

JIT_SYMBOL_NOT_FOUND 

A JIT symbol lookup found no symbol of that name.

JIT_SYMBOL_ALREADY_DEFINED 

A JIT symbol definition collided with a name already defined.

EMPTY_NAMESPACE_NAME 
EMPTY_SYMBOL_NAME 
STRING_LENGTH_LIMIT_EXCEEDED 
WRONG_RANGE 
EPOLL_INIT_FAILED 
EPOLL_EVENT_FD_FAILED 
EPOLL_FAILED_TO_ADD_FD 
CONNECTION_REFUSED 
CONNECTION_RESET 
CONNECTION_ABORTED 
BROKEN_PIPE 
BAD_DESCRIPTOR 
TIMED_OUT 
CANCELED 
NOT_CONNECTED 
ADDRESS_IN_USE 
HOST_UNREACHABLE 
NETWORK_UNREACHABLE 
MESSAGE_TOO_LONG 
TOO_MANY_OPEN_FILES 
PERMISSION_DENIED 
OUT_OF_MEMORY 
NOT_FOUND 
ALREADY_EXISTS 
IS_A_DIRECTORY 
NOT_A_DIRECTORY 
NO_SPACE 
INVALID_ARGUMENT 

The request itself is malformed (an empty POLL interest mask, for example), so retrying it unchanged can never succeed.

CHANNEL_BUSY 

The channel already has a full ring's worth of operations in flight.

Transient by construction, it clears as the worker consumes completions, so the caller can retry.

MEMORY_MAP_FAILED 
IR_PARSE_ERROR 
UNKNOWN_IO_ERROR 

Definition at line 92 of file errors.h.

92 {
93 ABSURD,
102 /// The JIT could not parse a module's LLVM IR or bitcode.
104 /// A JIT symbol lookup found no symbol of that name.
106 /// A JIT symbol definition collided with a name already defined.
115
116 // IO operation failures, reactor backends will maps their platform
117 // errors to these tags.
123 TIMED_OUT,
124 CANCELED,
133 NOT_FOUND,
137 NO_SPACE,
138 /// The request itself is malformed (an empty POLL interest mask, for
139 /// example), so retrying it unchanged can never succeed.
141 /// The channel already has a full ring's worth of operations in flight.
142 /// Transient by construction, it clears as the worker consumes
143 /// completions, so the caller can retry.
srn_error_tag_t
The kind of a failure.
Definition errors.h:92
@ AL_LIMIT_REACHED
Definition errors.h:96
@ EPOLL_EVENT_FD_FAILED
Definition errors.h:113
@ NOT_IMPLEMENTED
Definition errors.h:94
@ BROKEN_PIPE
Definition errors.h:121
@ SM_BUFFER_ID_EXHAUSTED
Definition errors.h:97
@ HOST_UNREACHABLE
Definition errors.h:127
@ CORRUPTED_SEQ
Definition errors.h:99
@ CORRUPTED_ARRAY_LIST
Definition errors.h:100
@ EMPTY_NAMESPACE_NAME
Definition errors.h:108
@ JIT_IR_PARSE_FAILED
The JIT could not parse a module's LLVM IR or bitcode.
Definition errors.h:103
@ NETWORK_UNREACHABLE
Definition errors.h:128
@ STRING_LENGTH_LIMIT_EXCEEDED
Definition errors.h:110
@ IS_A_DIRECTORY
Definition errors.h:135
@ INVALID_ARGUMENT
The request itself is malformed (an empty POLL interest mask, for example), so retrying it unchanged ...
Definition errors.h:140
@ CHANNEL_BUSY
The channel already has a full ring's worth of operations in flight.
Definition errors.h:144
@ ABSURD
Definition errors.h:93
@ WRONG_RANGE
Definition errors.h:111
@ JIT_SYMBOL_ALREADY_DEFINED
A JIT symbol definition collided with a name already defined.
Definition errors.h:107
@ PERMISSION_DENIED
Definition errors.h:131
@ CONNECTION_RESET
Definition errors.h:119
@ UNKNOWN_IO_ERROR
Definition errors.h:147
@ TOO_MANY_OPEN_FILES
Definition errors.h:130
@ JIT_SYMBOL_NOT_FOUND
A JIT symbol lookup found no symbol of that name.
Definition errors.h:105
@ NOT_CONNECTED
Definition errors.h:125
@ CONNECTION_REFUSED
Definition errors.h:118
@ FAILED_TO_ADD_MODULE
Definition errors.h:101
@ INDEX_OUT_OF_BOUND
Definition errors.h:98
@ SEQ_LIMIT_REACHED
Definition errors.h:95
@ CONNECTION_ABORTED
Definition errors.h:120
@ ADDRESS_IN_USE
Definition errors.h:126
@ EPOLL_FAILED_TO_ADD_FD
Definition errors.h:114
@ EPOLL_INIT_FAILED
Definition errors.h:112
@ BAD_DESCRIPTOR
Definition errors.h:122
@ OUT_OF_MEMORY
Definition errors.h:132
@ NOT_FOUND
Definition errors.h:133
@ CANCELED
Definition errors.h:124
@ NOT_A_DIRECTORY
Definition errors.h:136
@ TIMED_OUT
Definition errors.h:123
@ IR_PARSE_ERROR
Definition errors.h:146
@ EMPTY_SYMBOL_NAME
Definition errors.h:109
@ MEMORY_MAP_FAILED
Definition errors.h:145
@ MESSAGE_TOO_LONG
Definition errors.h:129
@ ALREADY_EXISTS
Definition errors.h:134
@ NO_SPACE
Definition errors.h:137

Function Documentation

◆ srn_errno_to_tag()

srn_error_tag_t srn_errno_to_tag ( int errno_value,
srn_error_tag_t default_tag )

Map a POSIX errno to error tag.

Note: an errno with no mapping yields default_tag, so the caller picks the fallback that fits its domain. Defined on POSIX platforms. a non-POSIX backend should supply its own native-code mapping.

Here is the caller graph for this function:

◆ srn_error_to_string()

char * srn_error_to_string ( const srn_context_t * ctx,
const srn_error_t * err )
nodiscard

Format err as a newly allocated string of the form [TAG]: msg, allocated in ctx.

Returns [UNKNOWN]: <null> when err is null.

Definition at line 133 of file errors.c.

133 {
134 PANIC_IF_NULL(err);
135 const char *tag = srn_error_tag_name(err->tag);
136 const char *msg = err->msg != nullptr ? err->msg : "<null>";
137
138 int header_len = snprintf(nullptr, 0, "[%s]: %s", tag, msg);
139 PANIC_IF(header_len < 0, "srn_error_to_string: formatting failed");
140
141 // Measure the trace lines before allocating so a single allocation covers
142 // the whole string.
143 int trace_len = 0;
144 char sym[256];
145 if (err->layer == SRN_ERR_RUNTIME_LAYER && err->as.frames != nullptr) {
146 for (uint16_t i = 0; i < err->as.frames->count; i++) {
147 void *addr = err->as.frames->return_addrs[i];
148 const char *name = srn_dbg_sym_name(addr, sym, sizeof(sym));
149 if (name != nullptr) {
150 trace_len += snprintf(nullptr, 0, "\n #%u %p %s", i, addr, name);
151 } else {
152 trace_len += snprintf(nullptr, 0, "\n #%u %p", i, addr);
153 }
154 }
155 }
156
157 size_t total = (size_t)header_len + (size_t)trace_len + 1;
158 char *buf = srn_allocate(ctx, total, alignof(char));
159 PANIC_IF_NULL(buf);
160
161 int written = snprintf(buf, total, "[%s]: %s", tag, msg);
162 if (err->layer == SRN_ERR_RUNTIME_LAYER && err->as.frames != nullptr) {
163 for (uint16_t i = 0; i < err->as.frames->count; i++) {
164 void *addr = err->as.frames->return_addrs[i];
165 const char *name = srn_dbg_sym_name(addr, sym, sizeof(sym));
166 if (name != nullptr) {
167 written += snprintf(buf + written, total - (size_t)written, "\n #%u %p %s", i, addr, name);
168 } else {
169 written += snprintf(buf + written, total - (size_t)written, "\n #%u %p", i, addr);
170 }
171 }
172 }
173
174 return buf;
175}
static atomic_int total
Definition 05_parallel.c:40
void * srn_allocate(const srn_context_t *ctx, size_t size, size_t alignment)
Allocate size bytes aligned to alignment from the context's memory block.
Definition context.c:73
static const char * srn_error_tag_name(srn_error_tag_t tag)
Definition errors.c:36
const char * srn_dbg_sym_name(void *addr, char *buf, size_t buflen)
Resolve addr to a symbol name and write it into buf (null-terminated, at most buflen bytes including ...
Definition srn_dbg_sym.c:26
union srn_error_t::@110141362101221075206211275212023153042233307220 as
srn_error_layer_t layer
Definition errors.h:174
char * msg
Definition errors.h:176
srn_error_tag_t tag
Definition errors.h:175
srn_trace_frames_t * frames
Definition errors.h:181
uint16_t count
Definition errors.h:166
void * return_addrs[TRACE_FRAME_DEPTH]
Definition errors.h:165
#define PANIC_IF_NULL(ptr)
Definition utils.h:66
#define PANIC_IF(cond, msg)
Definition utils.h:59
Here is the call graph for this function:
Here is the caller graph for this function:

◆ srn_error_trace_push()

void srn_error_trace_push ( const srn_context_t * ctx,
srn_error_t * err,
void * addr )

Append a return address to the trace of a runtime-layer error.

Allocates frames from ctx on the first call. Silently does nothing when the trace is full or the error is not a runtime-layer error.

Definition at line 177 of file errors.c.

177 {
178 if (err->layer != SRN_ERR_RUNTIME_LAYER) {
179 return;
180 }
181
182 if (err->as.frames == nullptr) {
183 err->as.frames = ALLOC(ctx, srn_trace_frames_t);
184 PANIC_IF_NULL(err->as.frames);
185 err->as.frames->count = 0;
186 }
187
188 if (err->as.frames->count >= TRACE_FRAME_DEPTH) {
189 return;
190 }
191
192 err->as.frames->return_addrs[err->as.frames->count++] = addr;
193}
#define ALLOC(ctx, T)
Definition context.h:95
#define TRACE_FRAME_DEPTH
Definition errors.h:63
This is how we create a traceback for the C level code in the runtime lib.
Definition errors.h:164
Here is the caller graph for this function:

◆ srn_errors_fail()

void * srn_errors_fail ( const srn_context_t * ctx,
size_t size,
size_t alignment,
srn_error_tag_t tag,
const char * msg,
void * caller )
nodiscard

Allocate a size/alignment value in ctx, set its ERROR_HEADER (the first member, srn_error_t *maybe_error) to a fresh error, and return it.

The result is a valid fallible value whose HAS_ERROR is true. Use the FAIL macro, which fills in size/alignment from the target type.

Definition at line 224 of file errors.c.

227 {
228 // The ERROR_HEADER convention guarantees `srn_error_t *maybe_error` is the
229 // first member of any fallible T, so offset 0 of the value is that pointer.
230 // Allocate the value and set it to a fresh error; the caller checks it with
231 // HAS_ERROR and never reads the other fields of an errored value.
232 void *value = srn_allocate(ctx, size, alignment);
233 PANIC_IF_NULL(value);
234 *(srn_error_t **)value = srn_errors_make(ctx, tag, msg, caller);
235 return value;
236}
A runtime error, a tag classifying the failure and a human-readable message.
Definition errors.h:173
Here is the call graph for this function:

◆ srn_errors_from()

srn_error_t * srn_errors_from ( const srn_context_t * ctx,
const srn_error_t * src,
void * caller )

Build an error in ctx's memory that carries the layer, tag, message and payload of src, with a trace of its own that starts at caller.

src is not written to, so this is the way to take a trace on an error that is shared or immutable, such as the reactor's static IO errors. A runtime layer src with frames has them copied, so the earlier trace is kept. The ERR_FROM macro supplies caller.

Definition at line 207 of file errors.c.

207 {
208 PANIC_IF_NULL(src);
210 err->layer = src->layer;
211 err->tag = src->tag;
212 err->msg = src->msg;
213 err->as = src->as;
214
215 if (src->layer == SRN_ERR_RUNTIME_LAYER && src->as.frames != nullptr) {
217 *err->as.frames = *src->as.frames;
218 }
219
220 srn_error_trace_push(ctx, err, caller);
221 return err;
222}
#define ALLOC_OR_PANIC(ctx, T)
Definition context.h:98
Here is the call graph for this function:

◆ srn_errors_make()

srn_error_t * srn_errors_make ( const srn_context_t * ctx,
srn_error_tag_t tag,
const char * msg,
void * caller )

Build an error in ctx's memory, tagged tag and carrying msg.

caller is the return address of the call site (pass __builtin_return_address(0)) and is recorded as the first frame of the trace. The ERR macro supplies it automatically.

Definition at line 196 of file errors.c.

196 {
197 srn_error_t *err = ALLOC(ctx, srn_error_t);
198 PANIC_IF_NULL(err);
200 err->tag = tag;
201 err->msg = (char *)msg;
202 err->as.frames = nullptr;
203 srn_error_trace_push(ctx, err, caller);
204 return err;
205}
Here is the call graph for this function:
Here is the caller graph for this function: