1 /*
2  * Copyright (C) 2002-2003 Stefan Holst
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA.
17  *
18  * utilities
19  */
20 
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <sys/types.h>
25 #include <sys/time.h>
26 #include <errno.h>
27 #include <pthread.h>
28 
29 #include "list.h"
30 #include "utils.h"
31 
32 /*
33  * multi purpose scheduler
34  */
35 
36 typedef struct {
37 
38   time_t start_time;
39   xine_list_t *jobs;
40   pthread_t scheduler_thread;
41   pthread_mutex_t jobs_mutex;
42   pthread_mutex_t job_execution_mutex;
43   pthread_mutex_t wait_mutex;
44   pthread_cond_t jobs_reorganize;
45   int job_id;
46   int keep_going;
47 
48 } ox_scheduler_t;
49 
50 
51 /*
52  * GLOBAL scheduler instance
53  */
54 
55 static ox_scheduler_t *ox_scheduler = NULL;
56 
57 
58 typedef struct {
59 
60   struct timespec ts; /* when to do */
61   void (*cb)(void *data);
62   void *data;
63   int id;
64 
65 } job_t;
66 
scheduler_thread(void * data)67 static __attribute__((noreturn)) void *scheduler_thread(void *data) {
68 
69   job_t *job;
70   int ret;
71   void (*cb)(void *data);
72   void *cb_data;
73 
74 #ifdef LOG
75   printf("utils: scheduler thread created\n");
76 #endif
77 
78   if (!ox_scheduler) {
79     pthread_exit(NULL);
80   }
81   pthread_mutex_lock(&ox_scheduler->wait_mutex);
82 
83   while (ox_scheduler->keep_going) {
84 
85     pthread_mutex_lock(&ox_scheduler->jobs_mutex);
86     job = xine_list_last_content(ox_scheduler->jobs);
87 
88     if (!job) { /* no jobs for me */
89       pthread_mutex_unlock(&ox_scheduler->jobs_mutex);
90 #ifdef LOG
91       printf("utils: no jobs in queue\n");
92 #endif
93       pthread_cond_wait(&ox_scheduler->jobs_reorganize, &ox_scheduler->wait_mutex);
94       continue;
95     } else pthread_mutex_unlock(&ox_scheduler->jobs_mutex);
96 
97 #ifdef LOG
98     printf("utils: sleeping until next job\n");
99 #endif
100     ret = pthread_cond_timedwait(&ox_scheduler->jobs_reorganize,
101 	                         &ox_scheduler->wait_mutex, &job->ts);
102 
103     if (ret == ETIMEDOUT) {
104       /* lets do a job */
105       pthread_mutex_lock(&ox_scheduler->job_execution_mutex);
106       pthread_mutex_lock(&ox_scheduler->jobs_mutex);
107       job = xine_list_last_content(ox_scheduler->jobs);
108       cb = NULL;
109       cb_data = NULL;
110       if (job) {
111 #ifdef LOG
112         printf("utils: executing job %i\n", job->id);
113 #endif
114         cb = job->cb;
115         cb_data = job->data;
116       }
117       xine_list_delete_current(ox_scheduler->jobs);
118       pthread_mutex_unlock(&ox_scheduler->jobs_mutex);
119 
120       if (cb) cb(cb_data);
121 
122       pthread_mutex_unlock(&ox_scheduler->job_execution_mutex);
123 
124     } else {
125 #ifdef LOG
126       printf("utils: reorganizing queue\n");
127 #endif
128     }
129   }
130   pthread_mutex_unlock(&ox_scheduler->wait_mutex);
131   pthread_exit(NULL);
132 }
133 
start_scheduler(void)134 void start_scheduler(void) {
135 
136   struct timeval tv;
137   struct timezone tz;
138 
139   ox_scheduler = malloc(sizeof(ox_scheduler_t));
140 
141   gettimeofday(&tv, &tz);
142   ox_scheduler->start_time = tv.tv_sec;
143 
144   ox_scheduler->jobs = xine_list_new();
145   pthread_mutex_init(&ox_scheduler->wait_mutex, NULL);
146   pthread_mutex_init(&ox_scheduler->jobs_mutex, NULL);
147   pthread_mutex_init(&ox_scheduler->job_execution_mutex, NULL);
148   pthread_cond_init(&ox_scheduler->jobs_reorganize, NULL);
149   ox_scheduler->job_id = 0;
150   ox_scheduler->keep_going = 1;
151 
152   if (pthread_create(&ox_scheduler->scheduler_thread, NULL, scheduler_thread, ox_scheduler)) {
153     printf("utils: error creating scheduler thread\n");
154     abort();
155   }
156 }
157 
stop_scheduler(void)158 void stop_scheduler(void) {
159 
160   job_t *job;
161   void *ret = NULL;
162 
163   if (!ox_scheduler) return;
164 
165   ox_scheduler->keep_going = 0;
166   pthread_cond_signal(&ox_scheduler->jobs_reorganize);
167 
168   pthread_join(ox_scheduler->scheduler_thread, ret);
169 
170   job = xine_list_first_content(ox_scheduler->jobs);
171 
172   while(job) {
173 #ifdef LOG
174     printf("utils: cancelling pending job %i\n", job->id);
175 #endif
176     free(job);
177     job = xine_list_next_content(ox_scheduler->jobs);
178   }
179   xine_list_free(ox_scheduler->jobs);
180   pthread_mutex_destroy(&ox_scheduler->wait_mutex);
181   pthread_mutex_destroy(&ox_scheduler->jobs_mutex);
182   pthread_cond_destroy(&ox_scheduler->jobs_reorganize);
183 
184   free(ox_scheduler);
185   ox_scheduler = NULL;
186 }
187 
schedule_job(int delay,void (* cb)(void * data),void * data)188 int schedule_job(int delay, void (*cb)(void *data), void *data) {
189 
190   struct timeval tv;
191   struct timezone tz;
192   job_t *job;
193   int msec;
194   int priority;
195 
196   if (!ox_scheduler) return -1;
197 
198   job = malloc(sizeof(job_t));
199 
200   gettimeofday(&tv, &tz);
201 
202   job->ts.tv_sec = (delay / 1000) + tv.tv_sec;
203   msec = delay % 1000;
204   if ((msec + tv.tv_usec/1000)>=1000) job->ts.tv_sec++;
205   msec = (msec + tv.tv_usec/1000) % 1000;
206   job->ts.tv_nsec = msec * 1000000;
207 
208   job->cb = cb;
209   job->data = data;
210   job->id = ++ox_scheduler->job_id;
211 
212   priority = (job->ts.tv_sec - ox_scheduler->start_time) * 1000 + msec;
213 
214   pthread_mutex_lock(&ox_scheduler->jobs_mutex);
215   xine_list_append_priority_content(ox_scheduler->jobs, job, priority);
216   pthread_mutex_unlock(&ox_scheduler->jobs_mutex);
217 
218   pthread_cond_signal(&ox_scheduler->jobs_reorganize);
219 
220   return ox_scheduler->job_id;
221 }
222 
cancel_job(int job_id)223 void cancel_job(int job_id) {
224 
225   job_t *job;
226 
227   if (!ox_scheduler) return;
228 
229   pthread_mutex_lock(&ox_scheduler->jobs_mutex);
230 
231   job = xine_list_first_content(ox_scheduler->jobs);
232 
233   while(job) {
234     if (job->id == job_id) break;
235     job = xine_list_next_content(ox_scheduler->jobs);
236   }
237   if (job) {
238     xine_list_delete_current(ox_scheduler->jobs);
239 #ifdef LOG
240     printf("utils: job %i cancelled\n", job->id);
241 #endif
242     free(job);
243   }
244   pthread_mutex_unlock(&ox_scheduler->jobs_mutex);
245 
246   pthread_cond_signal(&ox_scheduler->jobs_reorganize);
247 }
248 
lock_job_mutex(void)249 void lock_job_mutex(void) {
250   if (!ox_scheduler) return;
251   pthread_mutex_lock(&ox_scheduler->job_execution_mutex);
252 }
253 
unlock_job_mutex(void)254 void unlock_job_mutex(void) {
255   if (!ox_scheduler) return;
256   pthread_mutex_unlock(&ox_scheduler->job_execution_mutex);
257 }
258 
259 /*
260  * heap management
261  */
262 
263 /*
264  * Heap objects are aligned on sizeof(int) boundaries
265  */
266 
267 #define ALIGNMENT (sizeof(int))
268 #define DOALIGN(num) (((num)+ALIGNMENT-1)&~(ALIGNMENT-1))
269 
270 /*
271  * tag datastructures
272  */
273 
274 typedef struct prefix_tag_s  prefix_tag_t;
275 typedef struct postfix_tag_s postfix_tag_t;
276 
277 struct prefix_tag_s {
278   prefix_tag_t *prev;         /* previous object in heap      */
279   prefix_tag_t* next;         /* next object in heap          */
280   postfix_tag_t* postfix;     /* ptr to postfix object        */
281   const char* filename;       /* filename ptr or NULL         */
282   long line;                  /* line number or 0             */
283   size_t size;                /* size of allocated block      */
284   void* content;              /* _gen_malloc() ptr of object  */
285   char* tag;                  /* description string or NULL   */
286 };
287 
288 struct postfix_tag_s {
289   prefix_tag_t* prefix;
290 };
291 
292 /*
293  * GLOBAL: Points to first object in linked list of heap objects
294  */
295 
296 static prefix_tag_t* heap_head=NULL;
297 
298 
299 static void AddToLinkedList      ( prefix_tag_t* );
300 static void RemoveFromLinkedList ( prefix_tag_t* );
301 static void RenderDesc           ( prefix_tag_t*, char* );
302 
303 
_gen_malloc(size_t wSize,const char * tag,const char * lpFile,int nLine)304 void *_gen_malloc(size_t wSize, const char* tag, const char* lpFile, int nLine) {
305 
306   prefix_tag_t* prefix;
307 
308   wSize = DOALIGN(wSize);
309   prefix=(prefix_tag_t*)malloc(sizeof(prefix_tag_t)+wSize+sizeof(postfix_tag_t));
310   if (prefix) {
311     AddToLinkedList( prefix );
312     prefix->postfix = (postfix_tag_t*)((char*)(prefix+1)+wSize);
313     prefix->postfix->prefix = prefix;
314     prefix->filename = lpFile;
315     prefix->line = nLine;
316     prefix->content = prefix+1;
317     prefix->size = wSize;
318     prefix->tag = NULL;
319     if (tag) prefix->tag = strdup(tag);
320     memset( prefix->content, 0, wSize );
321     }
322   else {
323     printf("utils: failed to alloc memory\n");
324     abort();
325   }
326 
327   return(prefix ? prefix+1 : NULL);
328 }
329 
330 
_gen_free(void * content)331 void *_gen_free(void* content) {
332 
333   if (ho_verify(content)) {
334 
335     prefix_tag_t* prefix=(prefix_tag_t*)content-1;
336     size_t        wSize=(char*)(prefix->postfix+1)-(char*)prefix;
337 
338     RemoveFromLinkedList( prefix );
339     free(prefix->tag);
340     memset( prefix, 0, wSize );
341     free(prefix);
342   }
343 
344   return (NULL);
345 }
346 
347 
_gen_strdup(const char * lpS,const char * lpFile,int nLine)348 void *_gen_strdup(const char* lpS, const char* lpFile, int nLine) {
349 
350   void* lpReturn=NULL;
351 
352   if (lpS) {
353     size_t wSize = (size_t)(strlen(lpS)+1);
354 
355     lpReturn = _gen_malloc( wSize, "strdup'ed string", lpFile, nLine );
356     if (lpReturn) {
357       memcpy( lpReturn, lpS, wSize );
358     }
359   }
360   return(lpReturn);
361 
362 }
363 
364 
_gen_realloc(void * lpOld,size_t wSize,const char * lpFile,int nLine)365 void *_gen_realloc(void* lpOld, size_t wSize, const char* lpFile, int nLine) {
366 
367   void* lpNew=NULL;
368 
369   /*--- Try to realloc ---*/
370   if (lpOld) {
371     if (ho_verify(lpOld)) {
372       prefix_tag_t* prefix=(prefix_tag_t*)lpOld-1;
373       prefix_tag_t* lpNewPrefix;
374       prefix_tag_t* lpPre;
375 
376       /*--- Try to reallocate block ---*/
377       RemoveFromLinkedList( prefix );
378       memset( prefix->postfix, 0, sizeof(postfix_tag_t) );
379       wSize = DOALIGN(wSize);
380       lpNewPrefix=(prefix_tag_t*)realloc(prefix,
381           sizeof(prefix_tag_t)+wSize+sizeof(postfix_tag_t));
382 
383       /*--- Add new (or failed old) back in ---*/
384       lpPre=(lpNewPrefix?lpNewPrefix:prefix);
385       AddToLinkedList( lpPre );
386       lpPre->postfix = (postfix_tag_t*)((char*)(lpPre+1)+wSize);
387       lpPre->postfix->prefix = lpPre;
388       lpPre->content = lpPre+1;
389       lpPre->size = wSize;
390 
391       /*--- Finish ---*/
392       lpNew = (lpNewPrefix ? &lpNewPrefix[1] : NULL);
393       if (!lpNew) {
394 	printf("utils: failed to alloc memory\n");
395 	abort();
396       }
397     }
398   }
399 
400   /*--- Else try new allocation ---*/
401   else {
402     lpNew = _gen_malloc( wSize, NULL, lpFile, nLine );
403     }
404 
405   /*--- Return address to object ---*/
406   return(lpNew);
407 
408 }
409 
410 
heapstat(void)411 void heapstat(void) {
412 
413   unsigned long total = 0;
414   unsigned long chunks = 0;
415   if (heap_head) {
416     prefix_tag_t* lpCur=heap_head;
417 
418     while (ho_verify(&lpCur[1])) {
419       char buffer[100];
420 
421       RenderDesc( lpCur, buffer );
422       /*--- print out buffer ---*/
423       printf( "heapstat: %s\n", buffer );
424       total+=lpCur->size;
425       chunks++;
426       lpCur = lpCur->next;
427       if (lpCur==heap_head) {
428         break;
429       }
430     }
431     if (total)
432       printf("heapstat: memory usage: %lu words in %lu chunks\n", total, chunks);
433   }
434 }
435 
436 
AddToLinkedList(prefix_tag_t * lpAdd)437 static void AddToLinkedList(prefix_tag_t* lpAdd) {
438     /*--- Add before current head of list ---*/
439     if (heap_head) {
440         lpAdd->prev = heap_head->prev;
441         (lpAdd->prev)->next = lpAdd;
442         lpAdd->next = heap_head;
443         (lpAdd->next)->prev = lpAdd;
444         }
445 
446     /*--- Else first node ---*/
447     else {
448         lpAdd->prev = lpAdd;
449         lpAdd->next = lpAdd;
450         }
451 
452     /*--- Make new item head of list ---*/
453     heap_head = lpAdd;
454 }
455 
RemoveFromLinkedList(prefix_tag_t * lpRemove)456 static void RemoveFromLinkedList(prefix_tag_t* lpRemove) {
457 
458     /*--- Remove from doubly linked list ---*/
459     (lpRemove->prev)->next = lpRemove->next;
460     (lpRemove->next)->prev = lpRemove->prev;
461 
462     /*--- Possibly correct head pointer ---*/
463     if (lpRemove==heap_head) {
464         heap_head = ((lpRemove->next==lpRemove) ? NULL :
465             lpRemove->next);
466         }
467 }
468 
ho_is_ok(void * content)469 static int ho_is_ok(void* content)
470 {
471     return ((content) && (!((long)content&(ALIGNMENT-1))));
472 }
473 
474 
ho_verify(void * content)475 int ho_verify(void *content) {
476   int bOk=0;
477 
478   if (content) {
479     if (ho_is_ok(content)) {
480       prefix_tag_t* prefix=(prefix_tag_t*)content-1;
481       if(prefix->content==content) {
482         if(prefix->postfix->prefix==prefix) {
483           bOk = 1;
484 	}
485       }
486     }
487   }
488   return (bOk);
489 }
490 
491 
RenderDesc(prefix_tag_t * prefix,char * lpBuffer)492 static void RenderDesc(prefix_tag_t* prefix, char* lpBuffer) {
493   if (prefix->content==&prefix[1]) {
494     sprintf( lpBuffer, "%zu words @ %p:", prefix->size, prefix->content );
495     if (prefix->filename) {
496       sprintf( lpBuffer+strlen(lpBuffer), "%s:%ld ",
497 	  prefix->filename, prefix->line );
498     }
499     if (prefix->tag)
500       strcat(lpBuffer, prefix->tag);
501   }
502   else {
503     strcpy( lpBuffer, "(bad pointer given)" );
504   }
505 }
506