]> rtime.felk.cvut.cz Git - frescor/fwp.git/blob - frsh_aquosa/synchobj.c
bff5edc89f881020c11161fbbd7d6496b3a3514d
[frescor/fwp.git] / frsh_aquosa / synchobj.c
1
2 //synchobj_repo_t synchobj_repo;        /* defined downward, after the type definition! */
3
4 /*
5  * synchronization objects repository
6  *
7  * data structure and macro declaration for the handling of the repository
8  * of synchronization objects needed by the API implementation.
9  * It's used only in the core API module so no problem implementing it all
10  * in this file without affecting the header files in frsh/include with
11  * such an implementation specific and tighted issue
12  *
13  * Note that mutual exclusive access is implemented both for the repository
14  * as a whole and for each single element to maximize the achievable
15  * parallelism and concurrency execution of threads
16  *
17  * Note also the synchronization object (subsection of the core) API is
18  * implemented within this file, without any request to the service thread,
19  * and so all "remain" local to a single process!
20  */
21
22 /* data structures */
23
24 typedef struct synchobj_repo_entry {
25         frsh_mutex_t mutex;
26         fosa_cond_t sync;
27         int events;
28         int queued;
29 } synchobj_repo_entry_t;
30
31 typedef struct synchobj_repo {
32         synchobj_repo_entry_t repo[FRSH_MAX_N_SYNCH_OBJECTS];
33         int synchobj_number;
34         frsh_mutex_t mutex;
35 } synchobj_repo_t;
36
37 synchobj_repo_t synchobj_repo;  /* global variable */
38
39 /* macro and functions */
40
41 /*
42  * access functions to the repository
43  *
44  * some simple macro which help keep synchobj_repo_t opaque
45  * for the sake of code cleanness
46  */
47
48 /* given the handle of a synchronization object returns its index into the
49  * repository (frsh_synchobj_handle_t remains opaque) */
50 #define synchobj_handle_2_ind(h) \
51  ( h )
52 /* given the index of a synchronization object in the repository returns
53  * an handle to it (frsh_synchobj_handle_t remains opaque)*/
54 #define synchobj_ind_2_handle(i) \
55  ( i )
56 /* given a handle of a synchronization object returns a pointer to
57  * its entry into the repository */
58 #define get_synchobj_entry(handle) \
59 ( & ( synchobj_repo.repo[synchobj_handle_2_ind(handle)] ) )
60 /* return a pointer to the synchronization object repository mutex */
61 #define get_synchobj_repo_mutex() \
62  ( & ( synchobj_repo.mutex ) )
63 /* gives the actual number of synchronization objects in the repository */
64 #define get_synchobj_number() \
65  ( synchobj_repo.synchobj_number )
66
67 /*
68  * consistency and status checks
69  *
70  * common used checkings and tests about the access to the repository
71  * and the status of each entry
72  */
73
74 /* are we accessing the repository with a valid index value ? */
75 #define check_synchobj_repo_ind(ind) \
76  ( ( ind >= 0 && ind < FRSH_MAX_N_SYNCH_OBJECTS ) ? true: false )
77 /* is the repository entry (correctly accessed and) free ? */
78 #define check_synchobj_repo_entry_free(ind)                     \
79  ( ( (check_synchobj_repo_ind(ind)) &&                          \
80   synchobj_repo.repo[ind].events == -1 ) ? true : false )
81 /* is the repository entry (correctly accessed and) non free ? */
82 #define check_synchobj_repo_entry_nonfree(ind)                  \
83  ( ( (check_synchobj_repo_ind(ind)) &&                          \
84   synchobj_repo.repo[ind].events != -1 ) ? true : false )
85
86 /*
87  * initialize the repository
88  *
89  * sets all entries to invalid (that is, free) and init
90  * the "global" mutexe
91  *
92  * possible return values:
93  *  true (all ok)
94  *  false (something wrong with the mutex)
95  */
96 static inline bool synchobj_repo_init()
97 {
98         int i;
99
100         if (fosa_mutex_init(&synchobj_repo.mutex, 0) != 0)
101                 return false;
102
103         synchobj_repo.synchobj_number = 0;
104         for (i = 0; i < FRSH_MAX_N_SYNCH_OBJECTS; i++) {
105                 synchobj_repo.repo[i].events = -1;
106                 synchobj_repo.repo[i].queued = -1;
107         }
108
109         return true;
110 }
111
112 /*
113  * add an entry
114  *
115  * adds an entry into the repository. If successful the index of the entry is
116  * returned in vres_ind.
117  *
118  * We suppose the caller where to place the new entry and "suggests" us a
119  * free repository entry in synchobj_ind. We then check if it's really free
120  * and, if yes, we add the entry right there.
121  * If, on the other way, the entry is not free we need to search the whole
122  * repository for a suitable place.
123  *
124  * possible return values:
125  *  true (entry correctly added)
126  *  false (entry not added, maybe no more room in the repository!)
127  */
128 static inline bool synchobj_repo_put(int *synchobj_ind)
129 {
130
131         int i;
132         bool result;
133
134         /* lock the repository */
135         fosa_mutex_lock(&synchobj_repo.mutex);
136         /* try to place the new entry in the caller provided position (if any)
137          * and, only if it's not free, scan all the repository for
138          * a different and suitable place */
139         if (synchobj_ind != NULL &&
140                         check_synchobj_repo_entry_free(*synchobj_ind % FRSH_MAX_N_SYNCH_OBJECTS))
141                 i = *synchobj_ind % FRSH_MAX_N_SYNCH_OBJECTS;
142         else {
143                 i = 0;
144                 while (i < FRSH_MAX_N_SYNCH_OBJECTS && !check_synchobj_repo_entry_free(i))
145                         i++;
146         }
147         /* if we're sure it's valid and free we place the
148          * thread entry into the repository in position 'i' */
149         result = false;
150         if (check_synchobj_repo_entry_free(i)) {
151                 if (fosa_mutex_init(&synchobj_repo.repo[i].mutex, 0) != 0)
152                         return false;
153                 if (fosa_cond_init(&synchobj_repo.repo[i].sync) != 0)
154                         return false;
155                 synchobj_repo.repo[i].events = 0;
156                 synchobj_repo.repo[i].queued = 0;
157                 if (synchobj_ind != NULL)
158                         *synchobj_ind = i;
159                 result = true;
160         }
161
162         /* unlock the repository */
163         fosa_mutex_unlock(&synchobj_repo.mutex);
164
165         return result;
166 }
167
168 /*
169  * remove an entry
170  *
171  * sets the entry as free (if it already is not) and decrement the counter
172  *
173  * possible return values:
174  *  true (all ok, entry was non-free and is now free)
175  *  false (something wrong, entry was already free or index is out of renge)
176  */
177 static inline bool synchobj_repo_free(const int synchobj_ind)
178 {
179         bool result;
180
181         /* lock the whole repository */
182         fosa_mutex_lock(&synchobj_repo.mutex);
183
184         result = false;
185         if (check_synchobj_repo_entry_nonfree(synchobj_ind)) {
186                 fosa_mutex_destroy(&synchobj_repo.repo[synchobj_ind].mutex);
187                 fosa_cond_destroy(&synchobj_repo.repo[synchobj_ind].sync);
188                 synchobj_repo.repo[synchobj_ind].events = -1;
189                 synchobj_repo.repo[synchobj_ind].queued = -1;
190                 synchobj_repo.synchobj_number--;
191                 result = true;
192         }
193
194         /* unlock the whole repository */
195         fosa_mutex_unlock(&synchobj_repo.mutex);
196
197         return result;
198 }
199
200 /*
201  * is an entry free ?
202  *
203  * check if a specific entry of the repository can be considered free
204  * (we need do it with an atomic lock)
205  *
206  * possible return values:
207  *  true (entry is free)
208  *  false (entry is not free or index is out of range)
209  */
210 static inline bool synchobj_repo_isfree(const int synchobj_ind)
211 {
212         bool result;
213
214         /* lock the repository */
215         fosa_mutex_lock(&synchobj_repo.mutex);
216
217         result = check_synchobj_repo_entry_free(synchobj_ind);
218
219         /* unlock the repository */
220         fosa_mutex_unlock(&synchobj_repo.mutex);
221
222         return result;
223 }
224
225 /////////////////////////////////////////////////
226 // S Y N C H R O N I Z A T I O N   O B J E C T S
227 /////////////////////////////////////////////////
228
229 /*
230  * This section is implemented much differently from how it's described
231  * in the docs since the synchronization object are not unusable from
232  * INDETERMINATE workload type thread (remember we actually we're
233  * only handling such a type of vres!!), but they simply provide, for them,
234  * a conditional [timed] wait/signal mechanism as available and implemented in
235  * many OSs and threading library.
236  *
237  * In INDETERMINATE workloads, of course, a wait request on an synchronization
238  * object (or a timed wait request) does not imply the end of the current job
239  * nor cause any budget to be "returned", since no jobs are defined for them.
240  *
241  * In BUONDED workloads, when they'll be implemented here, we can flawlessy
242  * realize the API docs' semantic and consider end-job signals and budget
243  * return
244  *
245  * Note that, beside the peculiarity of this implementation being the
246  * orthogonality with respect to threads and processes, all the
247  * synchronization objects API section is implemented without bothering the
248  * service thread, so it remains local to the single process and usable from
249  * its various threads to synchronize themself, i.e. it is by no way usable in
250  * order to synchronize different processes or different threads among
251  * different processes between each other!
252  */
253
254 /*
255  * frsh_synchobj_create(), create a synchronization object
256  *
257  * Note FRSH_ERR_NOT_SCHEDULED_CALLING_THREAD and
258  * FRSH_ERR_INVALID_SCHEDULER_REPLY are never returned as errors within
259  * this implementation
260  *
261  * possible return values:
262  *  FRSH_NO_ERROR
263  *  FRSH_ERR_NOT_INITIALIZED
264  *  FRSH_ERR_BAD_ARGUMENT (NULL synchronization object handle)
265  *  FRSH_ERR_TOO_MANY_SYNCH_OBJECTS
266  *  FRSH_ERR_INTERNAL_ERROR (something, different from previous, went wrong)
267  */
268 int frsh_synchobj_create(frsh_synchobj_handle_t *synch_handle)
269 {
270         int synch_ind;
271
272 #ifdef DEBUG
273         strncpy(FUNCNAME, "frsh_synchobj_create", 21);
274 #endif
275
276         /* check for framework initialization and arguments */
277         if (!frsh_initialized)
278                 PERROR_AND_RETURN(FRSH_ERR_NOT_INITIALIZED,
279                                 "can't proceed before initializing FRSH with 'frsh_init()'!");
280         if (synch_handle == NULL)
281                 PERROR_AND_RETURN(FRSH_ERR_BAD_ARGUMENT,
282                                 "can't return a synchronization object in a NULL synch_handle");
283
284         /* add the object and return an handler to it */
285         if (synchobj_repo_put(&synch_ind))
286                 *synch_handle = synchobj_ind_2_handle(synch_ind);
287         else
288                 /* something wrong, try to guess what */
289                 if (get_synchobj_number() >= FRSH_MAX_N_SYNCH_OBJECTS)
290                         /* synchronization object max number reached */
291                         PERROR_AND_RETURN(FRSH_ERR_TOO_MANY_SYNCH_OBJS,
292                                         "can't create any more synchronization objects");
293                 else
294                         /* unspecified error (this should almost never be reached) */
295                         PERROR_AND_RETURN(FRSH_ERR_INTERNAL_ERROR,
296                                         "can't create the synchronization objects");
297
298         return FRSH_NO_ERROR;
299 }
300
301 /*
302  * frsh_synchobj_destroy(), destroy a synchronization object
303  *
304  * Note we check if wakeing up some threads is needed and we do this while
305  * holding a lock on the synchronization object, acquired while holding
306  * a lock on the whole repository in order to prevent races
307  *
308  * Note FRSH_ERR_NOT_SCHEDULED_CALLING_THREAD and
309  * FRSH_ERR_INVALID_SCHEDULER_REPLY are never returned as errors within
310  * this implementation too
311  *
312  * possible return value:
313  *  FRSH_NO_ERROR
314  *  FRSH_ERR_NOT_INITIALIZED
315  *  FRSH_ERR_INVALID_SYNCH_OBJ_HANDLE
316  *  FRSH_ERR_INTERNAL_ERROR (something, different from previous, went wrong)
317  */
318 int frsh_synchobj_destroy(const frsh_synchobj_handle_t synch_handle)
319 {
320         /* check for framework initialization and arguments */
321         if (!frsh_initialized)
322                 PERROR_AND_RETURN(FRSH_ERR_NOT_INITIALIZED,
323                                 "can't proceed before initializing FRSH with 'frsh_init()'!");
324         if (synchobj_repo_isfree(synchobj_handle_2_ind(synch_handle)))
325                 PERROR_AND_RETURN(FRSH_ERR_INVALID_SYNCH_OBJ_HANDLE,
326                                 "can't destroy a synchronization object from an invalid synch_handle");
327
328         /* get an exclusive access lock to the synchronization object
329          * while holding the exclusive access to the whole repository for the
330          * sake of safetyness */
331         fosa_mutex_lock(get_synchobj_repo_mutex());
332         fosa_mutex_lock(&(get_synchobj_entry(synch_handle)->mutex));
333         fosa_mutex_unlock(get_synchobj_repo_mutex());
334         /* have I got to wake up someone ?? */
335         while (get_synchobj_entry(synch_handle)->queued > 0) {
336                 if (fosa_cond_signal(&(get_synchobj_entry(synch_handle)->sync)) != 0) {
337                         fosa_mutex_unlock(&(get_synchobj_entry(synch_handle)->mutex));
338                         fosa_mutex_unlock(get_synchobj_repo_mutex());
339                         PERROR_AND_RETURN(FRSH_ERR_INTERNAL_ERROR,
340                                         "can't wake up blocked threads while destroying the synchronization object");
341                 } else
342                         get_synchobj_entry(synch_handle)->queued--;
343         }
344         /* release the lock on the synchronization object */
345         fosa_mutex_unlock(&(get_synchobj_entry(synch_handle)->mutex));
346
347         /* remove the synchronization object from the repository */
348         if (!synchobj_repo_free(synchobj_handle_2_ind(synch_handle)))
349                 /* unspecified error (this should almost never be reached) */
350                 PERROR_AND_RETURN(FRSH_ERR_INTERNAL_ERROR,
351                                 "can't delete the synchronization objects");
352
353         return FRSH_NO_ERROR;
354 }
355
356 /*
357  * frsh_synchobj_wait() and frsh_synchobj_wit_with_timeout
358  *
359  * They're very very much similar between each other so we choose to implement
360  * them using a single generic function with a few more parameters to
361  * discriminate the two behaviours, avoiding a lot of code replication
362  */
363
364 static int synchobj_wait(bool with_timeoout,            /* forward declaration of the generic wait function */
365         const frsh_synchobj_handle_t synch_handle,
366         const struct timespec *abs_timeout,
367         bool *timed_out,
368         struct timespec *next_budget,
369         struct timespec *next_period,
370         bool *was_deadline_missed,
371         bool *was_budget_overran);
372
373 /*
374  * frsh_synchobj_wait(), stop the thread till signaled by another one
375  *
376  * Implements the standard behaviour of a wait for a condition to be verified
377  * and signaled by another thread of the process.
378  *
379  * As stated before is perfectly usable even by the INDETERMINATE workloads
380  * for whom it does not represent the 'end job' event.
381  *
382  * It's implemented simply by a call to the generic function coded below
383  *
384  * Note FRSH_ERR_NOT_SCHEDULED_CALLING_THREAD and
385  * FRSH_ERR_INVALID_SCHEDULER_REPLY are never returned as errors within
386  * this implementation and also FRSH_ERR_WORKLOAD_NOT_COMPATIBLE
387  * is ignored by us
388  *
389  * possible return values:
390  *  FRSH_NO_ERROR
391  *  FRSH_ERR_NOT_INITIALIZED
392  *  FRSH_ERR_INVALID_SYNCH_OBJ_HANDLE (invalid synch. object or too many thread queued on it)
393  *  all that frsh_thread_get_vres_id returns (FRSH_ERR_NOT_BOUND, )
394  *  FRSH_ERR_INTERNAL_ERROR (something wrong trying to wait on the object)
395  */
396 int frsh_synchobj_wait(const frsh_synchobj_handle_t synch_handle,
397         frsh_rel_time_t *next_budget,
398         frsh_rel_time_t *next_period,
399         bool *was_deadline_missed,
400         bool *was_budget_overran)
401 {
402         /* simply call the generic function asking for a wait without any timeout */
403         return synchobj_wait(false, synch_handle, NULL, NULL, next_budget, next_period,
404                         was_deadline_missed, was_budget_overran);
405 }
406
407 /*
408  * frsh_synchobj_wait_with_timeout(), stop the thread till a timeout or a signal by another one
409  *
410  * Implements the standard behaviour of a wait for a condition to be verified
411  * and signaled by another thread of the process with a max. timeout value
412  *
413  * It's exactly the same as 'frsh_synchobj_wait()' with the only difference of
414  * the timeout and so it's, again, implemented simply by a call to the generic
415  * function with the correct parameters
416  *
417  * Note FRSH_ERR_NOT_SCHEDULED_CALLING_THREAD and
418  * FRSH_ERR_INVALID_SCHEDULER_REPLY are never returned as errors within
419  * this implementation and also FRSH_ERR_WORKLOAD_NOT_COMPATIBLE
420  * is ignored by us
421  *
422  * possible return values:
423  *  FRSH_NO_ERROR
424  *  FRSH_ERR_NOT_INITIALIZED
425  *  FRSH_ERR_INVALID_SYNCH_OBJ_HANDLE (invalid synch. object or too many thread queued on it)
426  *  all that 'frsh_thread_get_vres_id()' returns (FRSH_ERR_NOT_BOUND, )
427  *  FRSH_ERR_INTERNAL_ERROR (something wrong trying to wait on the object)
428  */
429 int frsh_synchobj_wait_with_timeout(
430         const frsh_synchobj_handle_t synch_handle,
431         const frsh_abs_time_t *abs_timeout,
432         bool *timed_out,
433         frsh_rel_time_t *next_budget,
434         frsh_rel_time_t *next_period,
435         bool *was_deadline_missed,
436         bool *was_budget_overran)
437 {
438         /* simply call the generic function asking for a wait with the specified timeout */
439         return synchobj_wait(true, synch_handle, abs_timeout, timed_out, next_budget, next_period,
440                         was_deadline_missed, was_budget_overran);
441 }
442
443 /*
444  * generic function definition:
445  *  it _really_ implements both (depending on the parameters)
446  *  wait on synchronization objects API calls
447  */
448 static int synchobj_wait(bool with_timeout,     /* discriminating parameter between the two functions */
449         const frsh_synchobj_handle_t synch_handle,
450         const struct timespec *abs_timeout,
451         bool *timed_out,
452         struct timespec *next_budget,
453         struct timespec *next_period,
454         bool *was_deadline_missed,
455         bool *was_budget_overran)
456 {
457         frsh_thread_id_t thread_self;
458         qres_sid_t thread_sid;
459         qres_time_t qres_thread_budget;
460         struct timespec tmp_thread_budget, tmp_thread_period;
461         frsh_vres_id_t vres_id;
462         int frsh_status, timedwait_status;
463
464         /* check for framework initialization and arguments */
465         if (!frsh_initialized)
466                 PERROR_AND_RETURN(FRSH_ERR_NOT_INITIALIZED,
467                                 "can't proceed before initializing FRSH with 'frsh_init()'!");
468         /* check the thread is running with a negotiated contract */
469         thread_self = fosa_thread_self();
470         thread_sid = FRSH_QRES_NOT_VALID_SID;
471         vres_id = FRSH_NOT_VALID_VRES_ID;
472         /* ask AQuoSA directly, if it's succesfull we can avoid a call to
473          * the service thread that is much less overhead!! */
474         if (qres_get_sid(thread_self.linux_pid, thread_self.linux_tid, &thread_sid) == QOS_E_NOSERVER)
475                 /* maybe we're bounded to a background vres and, in order
476                  * of being sure, we need to ask the service thread :-( */
477                 if ( (frsh_status = frsh_thread_get_vres_id(thread_self, &vres_id)) != FRSH_NO_ERROR)
478                         PERROR_AND_RETURN(frsh_status,
479                                         "can't wait if the thread is not bound to any vres");
480         if (synchobj_repo_isfree(synchobj_handle_2_ind(synch_handle)))
481                 PERROR_AND_RETURN(FRSH_ERR_INVALID_SYNCH_OBJ_HANDLE,
482                                 "can't wait on an synchronization object via an invalid synch_handler");
483
484         /* acquire the exclusive access lock on the synchronization object
485          * while holding it on the whole repository */
486         fosa_mutex_lock(get_synchobj_repo_mutex());
487         fosa_mutex_lock(&(get_synchobj_entry(synch_handle)->mutex));
488         fosa_mutex_unlock(get_synchobj_repo_mutex());
489
490         /* check if we can wait on the object or the limit has already been reached */
491         if ((get_synchobj_entry(synch_handle)->events == 0) &&
492                         (get_synchobj_entry(synch_handle)->queued >= FRSH_MAX_N_VRES_IN_SYNCH_OBJECT)) {
493                 fosa_mutex_unlock(&(get_synchobj_entry(synch_handle)->mutex));
494                 PERROR_AND_RETURN(FRSH_ERR_INVALID_SYNCH_OBJ_HANDLE,
495                                 "can't queue more vres on this synchronization object");
496         }
497         /* we need to wait only if there are no ready events */
498         if (!get_synchobj_entry(synch_handle)->events) {
499                 /* inform we're going to suspend oursef! */
500                 get_synchobj_entry(synch_handle)->queued++;
501                 if (with_timeout) {
502                         *timed_out = false;
503                         timedwait_status = fosa_cond_timedwait(&(get_synchobj_entry(synch_handle)->sync),
504                                         &(get_synchobj_entry(synch_handle)->mutex),
505                                         abs_timeout);
506                         if (timedwait_status != 0) {
507                                 if (timedwait_status == ETIMEDOUT) {
508                                         /* no signal event before the timeout */
509                                         *timed_out = true;
510                                         get_synchobj_entry(synch_handle)->events++;     /* preventive increment */
511                                 } else {
512                                         /* some other strange error! */
513                                         fosa_mutex_unlock(&(get_synchobj_entry(synch_handle)->mutex));
514                                         PERROR_AND_RETURN(FRSH_ERR_INTERNAL_ERROR,
515                                                 "can't wait on the synchronization object");
516                                 }
517                         }
518                 } else
519                         if (fosa_cond_wait(&(get_synchobj_entry(synch_handle)->sync),
520                                         &(get_synchobj_entry(synch_handle)->mutex)) != 0) {
521                                 fosa_mutex_unlock(&(get_synchobj_entry(synch_handle)->mutex));
522                                 PERROR_AND_RETURN(FRSH_ERR_INTERNAL_ERROR,
523                                                 "can't wait on the syncrhonization object");
524                         }
525                 /* the synchronization object is still valid? */
526                 if (synchobj_repo_isfree(synchobj_handle_2_ind(synch_handle)))
527                         /* no, someone destroyed our synchronization object! */
528                         PERROR_AND_RETURN(FRSH_ERR_INVALID_SYNCH_OBJ_HANDLE,
529                                         "synchronization object no longer valid, it has been destroyed during wait");
530                 get_synchobj_entry(synch_handle)->queued--;
531         }
532         get_synchobj_entry(synch_handle)->events--;
533         /* all done, release the lock on the synchronization object */
534         fosa_mutex_unlock(&(get_synchobj_entry(synch_handle)->mutex));
535
536         /* now retreive timing (budget and period) parameters, don't care about
537          * overruns (remember we're always WT_INDETERMINATE) and make them 
538          * both 'false' */
539         if (next_period != NULL || (next_budget != NULL && thread_sid == -1)) {
540                 /* ask the service thread only if it's strictly necessary !! */
541                 if (vres_id == FRSH_NOT_VALID_VRES_ID)
542                         /* we need to know (from the service thread) the vres_id */
543                         if ( (frsh_status = frsh_thread_get_vres_id(thread_self, &vres_id)) != FRSH_NO_ERROR)
544                                 PERROR_AND_RETURN(frsh_status,
545                                                 "can't gather vres info for period and budget retrieval");
546                 if (frsh_vres_get_budget_and_period(vres_id,
547                         &tmp_thread_budget,
548                         &tmp_thread_period) != FRSH_NO_ERROR)
549                         PERROR_AND_RETURN(FRSH_ERR_INTERNAL_ERROR,
550                                         "can't get vres/contract budget and period");
551         }
552         if (next_budget != NULL) {
553                 if (thread_sid == FRSH_QRES_NOT_VALID_SID)
554                         /* background thread, we use the contracted budget
555                          * retrieved previously */
556                         *next_budget = tmp_thread_budget;
557                 else {
558                         /* regular thread, ask for the actual budget direclty to AQuoSA
559                          * (much less overhead !!) */
560                         if (qres_get_curr_budget(thread_sid, &qres_thread_budget) != QOS_OK)
561                                 PERROR_AND_RETURN(FRSH_ERR_INTERNAL_ERROR,
562                                                 "can't get the actual budget for the vres");
563                         usec_to_timespec(*next_budget, qres_thread_budget);
564                 }
565         }
566         if (next_period != NULL)
567                 *next_period = tmp_thread_period;
568         if (was_deadline_missed != NULL)
569                 was_deadline_missed = false;
570         if (was_budget_overran != NULL)
571                 was_budget_overran = false;
572
573         return FRSH_NO_ERROR;
574 }
575
576 /*
577  * frsh_synchobj_signal(), notify a synchronization object
578  *
579  * Remember if noone is waiting for the notification it is queued in the
580  * synchronization objects
581  *
582  * Note FRSH_ERR_NOT_SCHEDULED_CALLING_THREAD and
583  * FRSH_ERR_INVALID_SCHEDULER_REPLY are never returned as errors within
584  * this implementation too
585  *
586  * possible return values:
587  *  FRSH_NO_ERROR
588  *  FRSH_ERR_NOT_INITIALIZED
589  *  FRSH_ERR_BAD_ARGUMENT (invalid synch_handle)
590  *  FRSH_ERR_TOO_MANY_EVENTS_IN_SYNCH_OBJ
591  */
592 int frsh_synchobj_signal(const frsh_synchobj_handle_t synch_handle)
593 {
594         /* check for framework initialization and arguments */
595         if (!frsh_initialized)
596                 PERROR_AND_RETURN(FRSH_ERR_NOT_INITIALIZED,
597                                 "can't proceed before initializing FRSH with 'frsh_init()'!");
598         if (synchobj_repo_isfree(synchobj_handle_2_ind(synch_handle)))
599                 PERROR_AND_RETURN(FRSH_ERR_BAD_ARGUMENT,
600                                 "can't signal a synchronization object via an invalid synch_handle");
601         /* acquire the exclusive access lock on the synchronization object
602          * while holding it on the whole repository */
603         fosa_mutex_lock(get_synchobj_repo_mutex());
604         fosa_mutex_lock(&(get_synchobj_entry(synch_handle)->mutex));
605         fosa_mutex_unlock(get_synchobj_repo_mutex());
606
607         if (get_synchobj_entry(synch_handle)->events >= FRSH_MAX_N_EVENTS_IN_SYNCH_OBJECT) {
608                 fosa_mutex_unlock(&(get_synchobj_entry(synch_handle)->mutex));
609                 PERROR_AND_RETURN(FRSH_ERR_TOO_MANY_EVENTS_IN_SYNCH_OBJ,
610                                 "can't enqueue more events on the synchronization object");
611         }
612         get_synchobj_entry(synch_handle)->events++;
613
614         /* release the lock */
615         fosa_mutex_unlock(&(get_synchobj_entry(synch_handle)->mutex));
616         /* and, finally, signal the condition */
617         fosa_cond_signal(&(get_synchobj_entry(synch_handle)->sync));
618         
619         return FRSH_NO_ERROR;
620 }
621
622 /*
623  * frsh_timed_wait(), sleep until specified absolute time
624  *
625  * Again, it does not imply 'end job' or any budget modification for
626  * INDETERMINATE workload vres, simply wait for the timer to fire!
627  *
628  * The timeout is implemented via the 'clock_nanosleep()' function
629  * (we need to consider a fosa wrapper for it too) in order to get rid
630  * of all the problems related to signal handling in a multithreaded
631  * environment (and even because it's the simplest and best suited
632  * solution for the acheiving what we need to!!)
633  *
634  * possible return values:
635  *  FRSH_NO_ERROR
636  *  FRSH_ERR_NOT_INITIALIZED
637  *  FRSH_ERR_BAD_ARGUMENT (NULL abs_time)
638  *  FRSH_ERR_INTERNAL_ERROR (FRSH_ERR_TIME_SPEC_IN_THE_PAST)
639  *  FRSH_ERR_INTERNAL_ERROR (something wrong with timer and/or signal handling)
640  *  all that 'frsh_thread_get_vres_id()' returns (FRSH_ERR_NOT_BOUND, )
641  */
642
643 int frsh_timed_wait (
644         const frsh_abs_time_t *abs_time,
645         frsh_rel_time_t *next_budget,
646         frsh_rel_time_t *next_period,
647         bool *was_deadline_missed,
648         bool *was_budget_overran)
649 {
650         frsh_thread_id_t thread_self;
651         qres_sid_t thread_sid;
652         qres_time_t qres_thread_budget;
653         struct timespec tmp_thread_budget, tmp_thread_period;
654         frsh_vres_id_t vres_id;
655         frsh_abs_time_t actual_time, rm_time;
656         int frsh_status;
657
658         /* check for framework initialization and arguments */
659         if (!frsh_initialized)
660                 PERROR_AND_RETURN(FRSH_ERR_NOT_INITIALIZED,
661                                 "can't proceed before initializing FRSH with 'frsh_init()'!");
662         if (abs_time == NULL)
663                 PERROR_AND_RETURN(FRSH_ERR_BAD_ARGUMENT,
664                                 "can't wait for a NULL abs_time");
665         /* check the thread is running with a negotiated contract */
666         thread_self = fosa_thread_self();
667         thread_sid = FRSH_QRES_NOT_VALID_SID;
668         vres_id = FRSH_NOT_VALID_VRES_ID;
669         if (qres_get_sid(thread_self.linux_pid, thread_self.linux_tid, &thread_sid) == QOS_E_NOSERVER)
670                 /* maybe we're bounded to a background vres and, in order
671                  * of being sure, we need to ask the service thread :-( */
672                 if ( (frsh_status = frsh_thread_get_vres_id(thread_self, &vres_id)) != FRSH_NO_ERROR)
673                         PERROR_AND_RETURN(frsh_status,
674                                         "can't get the vres id the calling thread is associated with");
675         /* check the provided absolute time reference is in the future */
676         if (fosa_clock_get_time(FOSA_CLOCK_REALTIME, &actual_time) < 0)
677                 PERROR_AND_RETURN(FRSH_ERR_INTERNAL_ERROR,
678                                 "can't get the current time");
679         if (frsh_abs_time_smaller(*abs_time, actual_time))
680                 //PERROR_AND_RETURN(FRSH_ERR_TIME_SPEC_IN_THE_PAST,
681                 PERROR_AND_RETURN(FRSH_ERR_INTERNAL_ERROR,
682                                 "can't wait for a past absolute time");
683         /* put the thread to sleep for the requested interval */
684         if (clock_nanosleep(FOSA_CLOCK_REALTIME, TIMER_ABSTIME, abs_time, &rm_time) < 0)
685                 PERROR_AND_RETURN(FRSH_ERR_INTERNAL_ERROR,
686                                 "can't put the thread to sleep");
687         /* if ( <check, via rm_time, if we've been interrupted> )
688          *      ... ... ...
689          *      ... ... ... */
690         /* retreive timing (budget and period) parameters, don't care about
691          * overruns (remember we're always WT_INDETERMINATE) and make them 
692          * both 'false' */
693         if (next_period != NULL || (next_budget != NULL && thread_sid == FRSH_QRES_NOT_VALID_SID)) {
694                 /* ask the service thread only if it's strictly necessary !! */
695                 if (vres_id == FRSH_NOT_VALID_VRES_ID)
696                         /* we need to know (from the service thread) the vres_id !! */
697                         if ( (frsh_status = frsh_thread_get_vres_id(thread_self, &vres_id)) != FRSH_NO_ERROR)
698                                 PERROR_AND_RETURN(frsh_status,
699                                                 "can't gather vres info for period and budget retrieval");
700                 if (frsh_vres_get_budget_and_period(vres_id,
701                         &tmp_thread_budget,
702                         &tmp_thread_period) != FRSH_NO_ERROR)
703                         PERROR_AND_RETURN(FRSH_ERR_INTERNAL_ERROR,
704                                         "can't get vres/contract budget and period");
705         }
706         if (next_budget != NULL) {
707                 if (thread_sid == FRSH_QRES_NOT_VALID_SID)
708                         /* background thread, we use the contracted budget
709                          * retrieved previously */
710                         *next_budget = tmp_thread_budget;
711                 else {
712                         /* regular thread, ask for the actual budget direclty to AQuoSA
713                          * (much less overhead !!) */
714                         if (qres_get_curr_budget(thread_sid, &qres_thread_budget) != QOS_OK)
715                                 PERROR_AND_RETURN(FRSH_ERR_INTERNAL_ERROR,
716                                                 "can't get the actual budget for the vres");
717                         usec_to_timespec(*next_budget, qres_thread_budget);
718                 }
719         }
720         if (next_period != NULL)
721                 *next_period = tmp_thread_period;
722         if (was_deadline_missed != NULL)
723                 *was_deadline_missed = false;
724         if (was_budget_overran != NULL)
725                 *was_budget_overran = false;
726
727         return FRSH_NO_ERROR;
728 }
729