summaryrefslogtreecommitdiff
path: root/doc/design-concepts.txt
blob: 1c8ba4617e81eb35592445b7e466a81bd2d5b2b3 (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
Design concepts for Mowgli
--------------------------

1. Object orientation

First and foremost, Mowgli is a base development framework that you can build
higher level development frameworks ontop of. To facilitate this in a way that
most people can understand, we use a somewhat LISP-like form of OO.

What does this mean?

In LISP, it is common for objects to be made up of smaller, more primitive
objects that help to facilitate the implementation of the object. What this
means is that, for example, we have mowgli_heap_t (which is a private object):

	struct mowgli_heap_
	{
		unsigned int elem_size;
		unsigned int mowgli_heap_elems;
		unsigned int free_elems;

		unsigned int alloc_size;

		unsigned int flags;

		mowgli_list_t blocks; /* list of non-empty blocks */

		mowgli_allocation_policy_t *allocator;
		mowgli_boolean_t use_mmap;

		mowgli_mutex_t mutex;

		mowgli_block_t *empty_block; /* a single entirely free block,
		                              * or NULL */
	};

This is an object which keeps state. It also depends on other objects, such as
allocation policies and lists. There is a need for a destructor, it has member
objects which have destructors, etc.

C allows you to make a pointer out of anything -- block-level variables, for
example can be "dereferenced" using the & operator. This means that if the heap
above did not require initialization, you could just write something like:

	{
		mowgli_heap_t heap;
		void *ptr;

		ptr = mowgli_heap_alloc(&heap);
		mowgli_heap_free(&heap, ptr);
	}

However, we have a need to initialize this object, so we provide a constructor
for it, mowgli_heap_create(). We also need to tear down the object
specifically, so we provide mowgli_heap_destroy().

While it is easy to provide a separate initializer function, we feel this would
be a bad idea for objects which keep state, because odds are likely the
destructor would not be called. There is also a stack memory limit -- which is
very small on embedded systems, where Mowgli is intended to be used.

There is also the possibility that the library may depend on the reference
remaining past the time it was used, such as an internal dependency (caching,
for example). So we want to enforce that a destructor is called when needed, or
at least make sure we don't crash because the object is no longer on the stack.

[Yes, I know you can put objects in BSS by making them 'static.' There is
limited space for BSS on many systems, so putting 100MB worth of objects there
is kind of insane and makes your program not look so great either.]

2. Threads are evil.

"The one-sentence answer is that humans are not smart enough to write
 thread-safe code."
	-- Rasmus Lerdorf, inventor of PHP

While PHP itself is a disaster of a programming language (for reasons I am not
going to go into here), Rasmus Lerdorf is entirely correct in the above quote.
While Mowgli is itself thread-safe, we do not recommend using threads as they
promote inferior design patterns. There are better, safer ways to achieve
concurrency using helpers and work queues with message-passing.

Mowgli's thread library implements a subset of a thread library. Specifically,
it implements the fastest possible mutex implementation, and global critical
sections ala a global lock. It does not implement anything else other than
that, except for maybe abstraction of TLS data in the future. But that TLS
support would just be to further advance Mowgli's thread-safety so that it is
more performant.

Threads are a gigantic hack. Depending on what you want, there is usually
something better for any pattern which involves threads:

Pattern: Threads which do work and then sleep for a long time.

Answer: Use mowgli_timer_add() on an eventloop object. This allows you to
queue deferred work as a first-class citizen of your app's eventloop.

Pattern: Multiple worker threads for I/O.

Answer: Using concurrency in an I/O bound app is entirely pointless, but you
can pass FDs to a helper using IPC. Pass the FD and then pass instructions on
what to do with it.

Pattern: Work that needs to be done concurrently.

Answer: Helpers with message-passing and specific regions of memory shared
between helpers, ala mmap() or VirtualAlloc().

Pattern: Work that needs to be done cooperatively.

Answer: Producer-consumer models can be implemented as first-class citizens
using the eventloop using pipes as a synchronization primitive. Alternatively,
use a coroutine library, such as the work-in-progress mowgli.coroutine.

Pretty much anything you can do with threads, you can do better with something,
*anything* else.

Please note the Mowgli team will generally not accept intrusive patches to
add any additional threading support aside from fixes and locking bugs. No
atomics, no making anything in the library use threads. Don't waste your time.