File: threading.cpp

package info (click to toggle)
falconpl 0.9.6.9-git20120606-2
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 46,176 kB
  • sloc: cpp: 181,389; ansic: 109,025; yacc: 2,310; xml: 1,218; sh: 403; objc: 245; makefile: 82; sql: 20
file content (462 lines) | stat: -rw-r--r-- 23,395 bytes parent folder | download | duplicates (2)
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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
/*
   FALCON - The Falcon Programming Language.
   FILE: threading.cpp

   Multithreading support - main file.
   -------------------------------------------------------------------
   Author: Giancarlo Niccolai
   Begin: Thu, 10 Apr 2008 00:44:09 +0200

   -------------------------------------------------------------------
   (C) Copyright 2008: the FALCON developers (see list in AUTHORS file)

   See LICENSE file for licensing details.
*/

/** \file
   Multithreading support - main file.
*/

#include <falcon/setup.h>
#include <falcon/module.h>
#include "version.h"
#include "threading_ext.h"
#include "threading_st.h"
#include <systhread.h>

/*#
   @module feathers.threading Multithreading
   @brief Multithreading Support Module.

   The @b threading provides support for real hardware-supported
   parallel execution of concurrent code under the same
   application hood; this is commonly called "Multithreading".

   By linking this module in the virtual machine through a load
   directive, or through the reflexive compiler module, the
   VM gets prepared to launch system threads and perform
   multithreading operations.

   See the related page @a threading_model for a description
   on the general aspects and design of the multithreading
   provided by this module.

   The @a threading_warnings page gives an important insight about
   overall security of multithreading applications (and
   specifically about MT security in Falcon).

   The @a threading_stop page explains about the conformance
   of this module with the VM Interruption protocol.
*/

/*#
   @beginmodule feathers.threading
*/

/*#
   @page threading_model Falcon multithreading model.
   
   @section threading_fore Forewords

   This document is by no mean an exhaustive explanation of multithreading
   in general. Concepts as "mutex", "synchronization primitive", "thread",
   and the like are given for granted. The reader should already know
   the basics of multithreading and have an idea on the topic, as this
   document just deals with the specificities of Falcon approach to
   multithreading.

   @section threading_basics Basic Principles

   Falcon multithreading is amied to maximize the efficiency of the VM
   running in a multithreading context, and of the execution of the scripts
   in separate threads.

   Principles of efficient and robust multithreading can be resumed in two points:
   - Threads must run unhindered and free from synchronization with the rest of
     the application for the vast part of their life. Data exchange with other
     threads must happen rarely, and it must take a fraction of the time needed
     to perform data elaboration.
   - Synchronization must happen at the topmost layers of the logic controlling
     threads, as it is a critical operation in their life, which must be given
     maximum care and control. Burying synchronization down in the lower layers
     of code, or worse, hiding its presence through class encapsulation is to be
     avoided.

   While real world is not perfect and there can be exceptions to this rules,
   using this two simple principles as a guide it is possible to write programs
   which maximally exploit the parallel computing facilities that modern computers
   provide, eliminating the risk of incurring in multithreading programming specific
   errors, as races, deadlocks and similar.

   Scripting languages, as Falcon, perform a lot of operations in background that
   are out of the control of the script writers, and this makes them a quite hard
   landscape for multithreading. A rule that can be considered a corollary to the
   two main principles, "mutexes must be held for the shortest possible time", is
   quite hard to be respected when the simplest istruction in a scripting language
   can take many complex actions (in the order of hundreds) at machine code level.

   For this reason, Falcon threading enforces the above principles with a "pure
   agent-based threading model".

   Each Falcon thread is an agent, bound to perform non-trivial and long-lasting
   operations, which can exchange data with the other agents in the application
   through objects called "synchronization structures". Structures are relatively
   complex objects that allow safe communication and interaction between threads.

   Each agent has its own application space where it is free to perform operations
   unhindered by the intervention of any other thread or by the Falcon engine.
   Exchange of data with the rest of the application can happen only thorough
   synchronization structures. Some of this structures are quite strightforward
   to be used, and in example, it is possible to share plain memory which can
   be directly manipulated by each thread as it prefers.

   @section mt_impl Multithreading implementation

   The Falcon threading module provides each agent with a new Virtual Machine
   created on the spot. Those VMs are created "empty", that is, they will
   contain only the modules that were linked by the VM that started the thread
   as it was right before starting its execution.

   VM related operations, as setting the garbage collection properties, termination
   requests, sleep requests, exceptions, memory pools and garbage collection loops
   are all local to a certain agent.

   Exchange of Falcon items, as objects, strings, vectors and so on is performed through
   serialization in memory. As each item lives and resides in a VM, each item must stay
   consistent to the VM that created it. Providing other agents the ability to change
   agent data would require synchronization at deep level in very frequent spots,
   and would rapidly make a script multithreading application run almost a thread
   at a time, instead of being fully parallel.

   However, the fact that items need to be serialized to be shared among agents doesn't
   necessarily means that they can't share memory. Falcon items are shells, representation
   of inner data which is provided by system-level code. While the items, which carry those
   data, must be copied between agents, the inner core which interacts with the system
   can just be shared and provided with proper synchronization at system level.

   In example, it is possible to share Falcon streams between agents. Each agent will have
   its own copy of the stream object, with a local view of stream data as i.e. the number
   of bytes written in the last operation, or the position in a file, but the underlying
   system resource will be shared and concurrency will be regulated by system calls.

   Embedding applications or other modules willing to work in multithreading can adopt the
   same strategy. Falcon gives the embedding applications and the module the ability to
   carry their data in a Falcon object, and receive relevant callbacks when a script
   needs to interact with that data. When an object is cloned for serialization, the embedding
   application will be notified, and it may perform proper action to prepare the data
   to be shared among threads. It may be as simple as a MT safe reference counting, or it may
   require more sophisticate operations; in example, sharing a Falcon Stream requires the inner
   layer of code to call a dup() system request to ask the operating system to create a
   duplicated file resource.

   Once shared between threads in this way, application or module data must take actions
   to ensure proper synchronization. That is, property access and method call must be
   prepared to be called by different threads concurrently.

   In this sense, it may be said that Falcon doesn't provide a "memory model", but allows
   each object to provide its own. While this may be thought as "confusing" for the script
   writers, once that the overall rules of the system (nothing is shared but...) plus the
   specific rules of the shared objects actually used by the script
   (... except this thing, when you do so) are known and followed, the overall complexity
   of a MT application built following this approach is by no mean higher than the complexity
   of a MT application built on a layer with a consistent and unique memory model.

   Overall complexity of a MT application depends on the data flow, and primarily on the
   synchronization logic and on the interactions between threads. There is nothing preventing
   an application with local, object specific memory models to be actually less complex than
   one with a burned-in memory model. The constraints given by each synchronization structure,
   which may have different visibility and sharing rules, ensure that a simple set of rules
   are valid locally, while the rest of the program is simply "safe and local". This actually
   works towards simplification and legibility of MT code.

   This may requires deep-level
   synchronization, which seems to contrast with the overall principles enunciated at the
   beginning of this section; but as long as this synchronization is kept minimal
   (i.e. just to
   ensure visibility of shared properties), or as long as the synchronization rules are
   available, known and controllable by the topmost level, the overall agent-based model
   is not broken.

   @section threading_sync_struct Synchronization structures

   Falcon agent-based model leverages on the concept of non-primitive structures used to
   synchronize and coordinate threads. Each structure has a different working principle which
   is explained in its detailed description, but they all share the concept of @b wait, @b acquisition
   and @b release.

   An agent can @b wait for one or more structures to be ready for acquisition. When this happens,
   the agent is notified and it acquires atomically one of the structures it was waiting for.

   Once perfomred the work for which the structure was needed, the agent must @b release the
   structure, and is then free to wait again or terminate.

   Acquisition and release are not necessarily bound to exclusive access. There are structures
   that may be acquired by many threads, or others that can only be acquired (their @b release is
   an void operation). The concept of @b acquisition is rather a semantic equivalent to "allowance",
   "permission" to perform certain operations bound with the structure.

   More details are explained in the description of each structure.
*/

/*#
   @page threading_warnings Multithreading safety
   @after threading_model
   @section tw_threading_can_break Multithreading can break things

   Falcon won't try to recover from multithreading errors done by the scripts. This means that
   an ill designed script can deadlock, mess up embedding application data and break in the most
   funny ways. If a script deadlocks, you won't be able to destroy its VM from an application
   and recover its resources; when a multithreaing thing (real full system level MT) in your
   application goes wild, your application is done for good. This even if the thing is a
   script encapsulated in a scripting engine.

   Also, this is not just unavoidable (don't believe in who says it can be avoided), but also
   desirable. When a MT problem happens, this leaves (or may have leaved) the application
   in an unconsistent state, an thus it may create errors in other parts of the application,
   or produce inconsistent results. If the application forcefully clears the inconsistent
   status of the MT sublayer, i.e. by forcefully releasing a mutex in deadlocked state,
   this has an high (and unmanageable) change to cause an unforecastable and possibly
   undetectable error somewhere else.

   As an example, think of a generic algorithm that locks a mutex, changes the name
   and the surname in a record and unlocks the mutex. Just, let's suppose we are able
   to change the name, but the surname is too long and the update method raises
   an exception. With automatic unlock, we'd have something like the following:

   @code
      // this is pseudo code; we're using a java/c# like syntax
      void updateRecord( String name, String surname )
                         throws something
      {
         AutoLock locker( this.mutex );
         this.setName( name );
         this.setSurname( surname ); // throws the exception.
      }
   @endcode

   In the above example we ipotized an AutoLock class that locks a mutex, then unlocks it
   at stack unroll.

   Now, that guard ensure that we release the mutex at function exit, no matter how it exits.
   Pitifully, it can't grant data to be consistent. In example, we may have set our record
   to a new name, "Adam aVeryVeryLongStrangerSurname", and get the exception on the surname.
   Adam will be stored, and we'll have something as an unexistent "Adam Smith" in our database.
   We broken our data, and we may know that much later, in example when Mr. aVeryVeryLong...etc,
   our boss, fires us because we didn't send him his montly wage payroll,
   but "yo, ye mutex is unlocked"!

   Of course, we could have gotten the exception and fix things; but while we
   were fixing things we may also have properly released the mutex when it
   was consistent to do it.

   But the point is another. If we made such an error as forgetting to take care
   of the dirty record above, then the most immediate and effective diagniostic is that of
   being locked on it. Compare the above with this code:

   @code
   // this is pseudo code; we're using a java/c# like syntax
   void updateRecord( String name, String surname )
                        throws something
   {
      this.mutex.lock();
      this.setName( name );
      this.setSurname( surname ); // throws the exception.
      this.mutex.unlock();
   }
   @endcode

   The logic error is the same as above. We have broken the name, but now the MT
   layer is in inconsistent state, and the next lock to the mutex will cause
   the application to deadlock. Provided mutexes are used rationally, this
   means that we'll have a clear indication of what appened in the early stages
   of development.

   Everyone forgets to unlock mutexes, at spots, and when it happens it hurts,
   because you feel like an idiot. Feeling like an idiot is the worst thing
   for a programmer. This is very probably the reason why such automatic one-line
   savers have been invented. However, clearing the MT layer status without
   being able to be sure that the application built on the MT layer is clear too
   is a very dangerous operation.

   As such, Falcon doesn't provide it. If you acquire a synchronized structure
   you have to explicitly release it, or it won't be available for others to
   be acquired again. This is because it would be impossible, for Falcon, to
   know which part of the application state has been left inconsistent together
   with the synchronization structure. If Falcon release a shared resource
   in behalf of a failing thread, this may cause other working threads to
   use the shared data that was half worked and incomplete, and this may
   lead to disasters that are far worse than deadlock.

   The correct approach to MT failures is not that to hide them or to
   automatically "fix" (hide) them, it is that to cure the program generating
   them so that they don't happen anymore.

   @section tw_threading_can_break2 Multithreading can break things (again)

   We said that that
   an ill designed script can deadlock, mess up embedding application data and break in the most
   funny ways.

   This means that embedding applications must be carefull in allowing their scripts to use threads.
   In case of security sensible applications, the threading module should be pre-loaded by
   the application and provided only to those scripts that have an administrative or equivalent
   security level. Only trusted scripts should be allowed to run MT in a production environment.
*/

/*#
   @page threading_stop Threading module and VM Interruption protocol
   @after threading_warnings
   
   Interruptible operations will raise an InterruptedError if they receive
   an asynchronous interruption request from another thread while ingaged
   in a lengthy wait.

   The Thread.wait method conforms to the VM Interruption protocol,
   along with other operations that are declared by the core module or
   by other extensions. Other interruptible operations are, currently:
   - sleep function.
   - Stream.readAvailable
   - Stream.writeAvailable
   - Socket.readAvailable (at the moment, only on non-windows platforms)
   - Socket.writeAvailable (at the moment, only on non-windows platforms)

   We are working to extend the protocol to other VM-level and sustem level
   operations.

   Other than coming from embedding applications, interruption requests
   can be generated by the Thread.stop method, that will send an asynchronous
   interruption request to the target thread.
*/


FALCON_MODULE_DECL
{
   #define FALCON_DECLARE_MODULE self

   Falcon::Module *self = new Falcon::Module();
   self->name( "threading" );
   self->language( "en_US" );
   self->engineVersion( FALCON_VERSION_NUM );
   self->version( VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION );
   //====================================
   // Message setting
   #include "threading_st.h"

   //=================================================================
   // Threading class -- enumeration for threading functions.
   //
   Falcon::Symbol *c_threading = self->addClass( "Threading" );
   self->addClassMethod( c_threading, "wait", Falcon::Ext::Threading_wait ).asSymbol()->
      addParam("waitTime");
   self->addClassMethod( c_threading, "vwait", Falcon::Ext::Threading_vwait ).asSymbol()->
      addParam("structArray")->addParam("waitTime");
   self->addClassMethod( c_threading, "getCurrentID", Falcon::Ext::Threading_getCurrentID );
   self->addClassMethod( c_threading, "getCurrent", Falcon::Ext::Threading_getCurrent );
   self->addClassMethod( c_threading, "sameThread", Falcon::Ext::Threading_sameThread ).asSymbol()->
      addParam("thread");
   self->addClassMethod( c_threading, "start", Falcon::Ext::Threading_start ).asSymbol()->
      addParam("callable");

   //=================================================================
   // Waitable class.
   //
   Falcon::Symbol *c_waitable = self->addClass( "Waitable" );
   c_waitable->exported( false );
   self->addClassMethod( c_waitable, "release", Falcon::Ext::Waitable_release );

   //=================================================================
   // Thread class.
   //
   Falcon::Symbol *c_thread = self->addClass( "Thread", &Falcon::Ext::Thread_init );
   c_thread->getClassDef()->addInheritance( new Falcon::InheritDef( c_waitable ) );
   c_thread->setWKS( true );
   self->addClassMethod( c_thread, "start", Falcon::Ext::Thread_start );
   self->addClassMethod( c_thread, "stop", Falcon::Ext::Thread_stop );
   self->addClassMethod( c_thread, "detach", Falcon::Ext::Thread_detach );
   self->addClassMethod( c_thread, "wait", Falcon::Ext::Thread_wait ).asSymbol()->
      addParam("waitTime");
   self->addClassMethod( c_thread, "vwait", Falcon::Ext::Thread_vwait ).asSymbol()->
      addParam("structArray")->addParam("waitTime");
   self->addClassMethod( c_thread, "getError", Falcon::Ext::Thread_getError );
   self->addClassMethod( c_thread, "getReturn", Falcon::Ext::Thread_getReturn );
   self->addClassMethod( c_thread, "hadError", Falcon::Ext::Thread_hadError );
   self->addClassMethod( c_thread, "getThreadID", Falcon::Ext::Thread_getThreadID );
   self->addClassMethod( c_thread, "sameThread", Falcon::Ext::Thread_sameThread ).asSymbol()->
      addParam("otherThread");
   self->addClassMethod( c_thread, "terminated", Falcon::Ext::Thread_terminated );
   self->addClassMethod( c_thread, "detached", Falcon::Ext::Thread_detached );
   self->addClassMethod( c_thread, "join", Falcon::Ext::Thread_join );
   self->addClassMethod( c_thread, "getSystemId", Falcon::Ext::Thread_getSystemID );
   self->addClassMethod( c_thread, "setName", Falcon::Ext::Thread_setName ).asSymbol()->
      addParam("name");
   self->addClassMethod( c_thread, "getName", Falcon::Ext::Thread_getName );
   self->addClassMethod( c_thread, "toString", Falcon::Ext::Thread_toString );
   self->addClassProperty( c_thread, "run" );

   //=================================================================
   // Grant class.
   //
   Falcon::Symbol *c_grant = self->addClass( "Grant", Falcon::Ext::Grant_init );
   c_grant->getClassDef()->addInheritance( new Falcon::InheritDef( c_waitable ) );

   //=================================================================
   // Barrier class.
   //
   Falcon::Symbol *c_barrier = self->addClass( "Barrier", Falcon::Ext::Barrier_init );
   c_barrier->getClassDef()->addInheritance( new Falcon::InheritDef( c_waitable ) );
   self->addClassMethod( c_barrier, "open", Falcon::Ext::Barrier_open );
   self->addClassMethod( c_barrier, "close", Falcon::Ext::Barrier_close );

   //=================================================================
   // Event class.
   //
   Falcon::Symbol *c_event = self->addClass( "Event", Falcon::Ext::Event_init );
   c_event->getClassDef()->addInheritance( new Falcon::InheritDef( c_waitable ) );
   self->addClassMethod( c_event, "set", Falcon::Ext::Event_set );
   self->addClassMethod( c_event, "reset", Falcon::Ext::Event_reset );

   //=================================================================
   // Counter class.
   //
   Falcon::Symbol *c_synccount = self->addClass( "SyncCounter", Falcon::Ext::SyncCounter_init );
   c_synccount->getClassDef()->addInheritance( new Falcon::InheritDef( c_waitable ) );
   self->addClassMethod( c_synccount, "post", Falcon::Ext::SyncCounter_post ).asSymbol()->
      addParam("count");

   //=================================================================
   // SyncQueue class.
   //
   Falcon::Symbol *c_synq = self->addClass( "SyncQueue", Falcon::Ext::SyncQueue_init );
   c_synq->getClassDef()->addInheritance( new Falcon::InheritDef( c_waitable ) );
   self->addClassMethod( c_synq, "push", Falcon::Ext::SyncQueue_push ).asSymbol()->
      addParam("item");
   self->addClassMethod( c_synq, "pushFront", Falcon::Ext::SyncQueue_pushFront ).asSymbol()->
      addParam("item");
   self->addClassMethod( c_synq, "pop", Falcon::Ext::SyncQueue_pop );
   self->addClassMethod( c_synq, "popFront", Falcon::Ext::SyncQueue_popFront );
   self->addClassMethod( c_synq, "empty", Falcon::Ext::SyncQueue_empty );
   self->addClassMethod( c_synq, "size", Falcon::Ext::SyncQueue_size );

   //============================================================
   // Thread Error class
   Falcon::Symbol *error_class = self->addExternalRef( "Error" ); // it's external
   Falcon::Symbol *thread_cls = self->addClass( "ThreadError", Falcon::Ext::ThreadError_init );
   thread_cls->setWKS( true );
   thread_cls->getClassDef()->addInheritance(  new Falcon::InheritDef( error_class ) );

   //============================================================
   // Join Error class
   Falcon::Symbol *joinerr_cls = self->addClass( "JoinError", Falcon::Ext::JoinError_init );
   joinerr_cls->setWKS( true );
   joinerr_cls->getClassDef()->addInheritance(  new Falcon::InheritDef( error_class ) );

   //==================================================================
   // Service feature.
   //
   //self->publishService( &the_service );

   return self;
}

/* end of threading.cpp */