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