Subversion Repositories shark

Rev

Rev 690 | Rev 923 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 pj 1
/*
2
 * Project: S.Ha.R.K.
3
 *
4
 * Coordinators:
5
 *   Giorgio Buttazzo    <giorgio@sssup.it>
6
 *   Paolo Gai           <pj@gandalf.sssup.it>
7
 *
8
 * Authors     :
9
 *   Paolo Gai           <pj@gandalf.sssup.it>
10
 *   Massimiliano Giorgi <massy@gandalf.sssup.it>
11
 *   Luca Abeni          <luca@gandalf.sssup.it>
12
 *   (see the web pages for full authors list)
13
 *
14
 * ReTiS Lab (Scuola Superiore S.Anna - Pisa - Italy)
15
 *
16
 * http://www.sssup.it
17
 * http://retis.sssup.it
18
 * http://shark.sssup.it
19
 */
20
 
21
 
22
/**
23
 ------------
912 pj 24
 CVS :        $Id: func.h,v 1.17 2005-01-07 08:21:16 pj Exp $
2 pj 25
 
26
 File:        $File$
912 pj 27
 Revision:    $Revision: 1.17 $
28
 Last update: $Date: 2005-01-07 08:21:16 $
2 pj 29
 ------------
30
 
31
Kernel functions:
32
 
33
- Debug stuff
34
 
35
- Primitives called at initialization time
36
 
37
- Kernel global functions (scheduler, queues)
38
 
39
- Kernel VM hooks
40
 
41
- IRQ, errors & exception handling
42
 
43
- System management primitives
44
 
45
- Jet management primitives
46
 
47
- Task management primitives
48
 
49
- Mutex primitives
50
 
51
**/
52
 
53
/*
54
 * Copyright (C) 2000 Paolo Gai
55
 *
56
 * This program is free software; you can redistribute it and/or modify
57
 * it under the terms of the GNU General Public License as published by
58
 * the Free Software Foundation; either version 2 of the License, or
59
 * (at your option) any later version.
60
 *
61
 * This program is distributed in the hope that it will be useful,
62
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
63
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
64
 * GNU General Public License for more details.
65
 *
66
 * You should have received a copy of the GNU General Public License
67
 * along with this program; if not, write to the Free Software
68
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
69
 *
70
 */
71
 
72
#ifndef __KERNEL_FUNC_H__
73
#define __KERNEL_FUNC_H__
74
 
75
#include <ll/ll.h>
76
#include <ll/stdio.h>
77
#include <kernel/types.h>
78
#include <kernel/model.h>
79
#include <kernel/mem.h>
80
#include <signal.h>
81
#include <kernel/var.h>
82
#include <errno.h>
83
 
84
 
85
/*---------------------------------------------------------------------*/
86
/* Debug stuff                                                         */
87
/*---------------------------------------------------------------------*/
88
 
89
/* if a source use printk() it should include log.h not func.h */
90
#include <kernel/log.h>
91
 
79 pj 92
#include "ll/sys/cdefs.h"
93
 
94
__BEGIN_DECLS
95
 
2 pj 96
/*---------------------------------------------------------------------*/
97
/* Kernel global functions: initialization & termination...            */
98
/*---------------------------------------------------------------------*/
99
 
100
/*+ this function is called by __kernel_init__.
101
    It register the modules in the system at init time +*/
102
TIME __kernel_register_levels__(void *arg);
103
 
104
/*+ This function returns a level_des **. the value returned shall be
38 pj 105
    used to register a level module.
2 pj 106
 
38 pj 107
    The function is usually called at module registration time.  The
108
    function can also be called when the system is already started, to
109
    allow the implementation of dynamic module registration.  
110
 
111
    The argument must be the size of the data block that have to be allocated
112
 
113
    The function returns the number of the descriptor allocated for the module
114
    or -1 in case there are no free descriptors.
115
 
116
    The function also reserves a descriptor with size s, initialized
117
    with default function pointers.
118
 
119
+*/
120
LEVEL level_alloc_descriptor(size_t s);
121
 
122
/*+ This function release a level descriptor previously allocated using
123
  level_alloc_descriptor().
124
 
125
  The function returns 0 if the level has been freed, or -1 if someone is
126
  using it, -2 if the level has never been registered.
127
 
128
+*/
129
int level_free_descriptor(LEVEL l);
130
 
131
/* Call this if you want to say that your module is using module l
132
   (e.g., for calling its private functions) */
133
int level_use_descriptor(LEVEL l);
134
 
135
/* Call this when you no more need the module l */
136
int level_unuse_descriptor(LEVEL l);
137
 
2 pj 138
/*+ This function returns a resource_des **. the value returned shall be
139
    used to register a resource module. The function shall be called only at
140
    module registration time. It assume that the system is not yet
141
    initialized, so we shall not call sys_abort...                     +*/
142
RLEVEL resource_alloc_descriptor();
143
 
144
/*+ This function compute the command line parameters from the multiboot_info
145
    NOTE: this function modify the multiboot struct, so this function and
146
    __call_main__ are mutually exclusives!!! +*/
147
void __compute_args__(struct multiboot_info *mbi, int *_argc, char **_argv);
148
 
149
/*+ This function calls the standard C main() function, with a
150
    parameter list up to 100 parameters                        +*/
151
int __call_main__(struct multiboot_info *mb);
152
 
153
/*+ This task initialize the system and calls the main() with
154
    __call_mail__ .
155
    It should be created by a level registered in the system by
156
    __kernel_register_levels__ +*/
157
TASK __init__(void *arg);
158
 
159
/*+ Use this function to post your own exit operations
160
    (when uses some defines contained in const.h) +*/
161
int sys_atrunlevel(void (*func_code)(void *),void *parm, BYTE when);
162
 
163
/*---------------------------------------------------------------------*/
29 pj 164
/* Kernel global functions: scheduler,                                 */
2 pj 165
/*---------------------------------------------------------------------*/
166
 
167
/*+ This is the generic scheduler.
168
    The scheduler calls the appropriates level schedulers and then
169
    dispatch the task chosen. +*/
170
void scheduler(void);
171
 
172
/*+ called in the events to force the system to execute the scheduler at
173
    the end of an event list +*/
174
void event_need_reschedule();
175
 
176
void task_makefree(void *ret);
177
void check_killed_async(void);
178
 
179
int guarantee();
180
 
38 pj 181
void levels_init(void); /* see init.c */
182
 
2 pj 183
void runlevel_init();
184
void call_runlevel_func(int runlevel, int aborting);
185
 
186
// in kill.c
187
void kill_user_tasks();
188
 
189
void event_resetepilogue();
190
 
191
 
192
void call_task_specific_data_destructors();
193
void task_specific_data_init();
194
 
195
// in kill.c
196
void register_cancellation_point(int (*func)(PID p, void *arg), void *arg);
197
 
198
// in signal.c
199
void register_interruptable_point(int (*func)(PID p, void *arg), void *arg);
200
 
201
 
202
/*---------------------------------------------------------------------*/
203
/* Kernel VM hooks                                                     */
204
/*---------------------------------------------------------------------*/
205
 
206
/* this hooks redefines the VM functions to use them into the kernel...
207
 
208
   the only VM functions called directly from the kernel are VM_init and
209
   VM_end
210
*/
211
 
646 mauro 212
/* Advanced Timer adjust */
213
#define kern_scale_timer       ll_scale_advtimer
214
 
620 mauro 215
/* Exit mode selection */
216
#define sys_set_reboot         ll_set_reboot
217
 
2 pj 218
/* Context management routines */
219
#define kern_context_create    ll_context_create
220
#define kern_context_delete    ll_context_delete
221
 
222
extern __inline__ CONTEXT kern_context_save()
223
{
224
  cli();
225
  return ll_context_from();
226
}
227
 
228
/*+ this functions are called every time a context is changed +*/
229
void kern_after_dispatch(void);
230
 
231
/* Warning: if modified, modify also:
232
   - task_join
233
   - cond_wait
234
   - cond_timedwait
235
   - internal_sem_wait
236
*/
82 pj 237
extern __inline__ void kern_context_load(CONTEXT c)
2 pj 238
{
239
  ll_context_to(c);
240
  kern_after_dispatch();
241
  sti();
242
}
243
 
244
 
245
/* Interrupt enabling/disabling */
246
#define kern_cli          cli
247
#define kern_sti          sti
248
 
249
/* Interrupt enabling/disabling with flag save */
250
#define kern_fsave        ll_fsave
251
#define kern_frestore     ll_frestore
252
 
253
/* interrupt handling */
254
#define kern_irq_unmask   VM_irq_unmask
255
#define kern_irq_mask     VM_irq_mask
256
 
257
extern __inline__ int kern_event_post(const struct timespec *time,
258
                                      void (*handler)(void *p),
259
                                      void *par)
260
{
261
  int e;
262
  e = event_post(*time,handler,par);
263
 
264
  if (e == -1)
265
    kern_raise(XNOMORE_EVENTS,exec_shadow);
266
 
267
  return e;
268
}
269
 
38 pj 270
#define kern_event_delete event_delete
271
 
2 pj 272
/*+ the default capacity timer used by the kernel... +*/
273
void capacity_timer(void *arg);
274
 
275
 
276
#define kern_printf message
277
 
38 pj 278
extern __inline__ TIME kern_gettime(struct timespec *t)
279
{
45 pj 280
  return ll_gettime(TIME_NEW, t);
38 pj 281
}
282
 
283
 
284
 
2 pj 285
/*---------------------------------------------------------------------*/
38 pj 286
/* Kernel global functions: IRQ handling                               */
2 pj 287
/*---------------------------------------------------------------------*/
288
 
289
/*+ Interrupt handler installation +*/
496 giacomo 290
int handler_set(int no, void (*fast)(int), PID pi, BYTE lock);
2 pj 291
 
292
/*+ Interrupt handler removal      +*/
293
int handler_remove(int no);
294
 
295
 
296
/*---------------------------------------------------------------------*/
297
/* System management primitives                                        */
298
/*---------------------------------------------------------------------*/
299
 
300
/*+ Close the system & return to HOST OS.
301
    Can be called from all the tasks...
302
    The first time it is called it jumps to the global context
303
    The second time it jumps only if there are no system task remaining
304
    The error code passed is 0... (it is saved on the first call!!!) +*/
305
void sys_end(void);
306
 
307
/*+ Close the system & return to HOST OS.
308
    Can be called from all the tasks...
309
    The first time it is called it works as the sys_end
310
    The second time it jumps every time
311
    The error code passed is 0... +*/
312
void sys_abort(int err);
313
 
571 giacomo 314
/*+ As sys_abort, but it works when the system is
315
    in shutdown mode  +*/
316
void sys_abort_shutdown(int err);
317
 
2 pj 318
/* The system implements also exit and _exit as a redefinition of sys_end
319
   This is not the correct behaviour, and should be fixed.
320
   The definitions for these functions are in kernel/kern.c
321
*/
322
 
323
/*+ Print a message then call sys_abort(333).
324
    Can be called from all the tasks...  +*/
325
void sys_panic(const char * fmt, ...) __attribute__ ((format (printf, 1, 2)));
326
 
327
/*+ prints an error message (see perror.c) +*/
328
void perror (const char *s);
329
 
330
/*+ this primitive returns the time read from the system timer +*/
331
TIME sys_gettime(struct timespec *t);
332
 
158 pj 333
/*+ this primitive can be used to set a message that will be printed
334
    at shutdown +*/
335
int sys_shutdown_message(char *fmt,...);
336
 
2 pj 337
/*---------------------------------------------------------------------*/
338
/* Jet management primitives                                           */
339
/*---------------------------------------------------------------------*/
340
 
341
/*+ This primitive returns the maximum execution time and the total
342
    execution time from the task_create or the last jet_delstat
343
    It returns also the number of instances to use to calculate the mean
344
    time and the current job execution time.
345
    The value returned is 0 if all ok, -1 if the PID is not correct or
346
    the task doesn't have the JET_ENABLE bit set.
347
+*/
348
int jet_getstat(PID p, TIME *sum, TIME *max, int *n, TIME *curr);
349
 
350
/*+ This primitive reset to 0 the maximum execution time and the mean
351
    execution time of the task p, and reset to 0 all the entries in
352
    jet_table.
353
    The value returned is 0 if all ok, -1 if the PID is not correct or
354
    the task doesn't have the JET_ENABLE bit set.                     +*/
355
int jet_delstat(PID p);
356
 
357
/*+ This primitive returns the last n values of the task execution time
358
    recorded after the last call to jet_gettable or jet_delstat.
359
    If n is
360
    <0 it will be set only the last values inserted in the table
361
       since the last call of jet_gettable.
362
    >0 it will be set up to JET_TABLE_DIM datas.
363
 
364
    The value returned is -1 if the PID is not correct or
365
    the task doesn't have the JET_ENABLE bit set, otherwise it returns the
366
    number of values set in the parameter table.
367
    (can be from 0 to JET_TABLE_DIM-1)
368
+*/
369
int jet_gettable(PID p, TIME *table, int n);
370
 
371
/*+ This function updates the jet information. +*/
372
void jet_update_slice(TIME t);
373
 
374
/*+ This function updates the jet information at the task end period
375
    it is called in task_endcycle and task_sleep +*/
376
void jet_update_endcycle();
377
 
378
/*---------------------------------------------------------------------*/
38 pj 379
/* Internal Macros                                                     */
380
/*---------------------------------------------------------------------*/
381
 
382
extern __inline__ void kern_epilogue_macro(void)
383
{
384
  TIME tx;    /* a dummy used for time computation             */
385
  struct timespec ty; /* a dummy used for time computation     */
386
 
387
  kern_gettime(&schedule_time);
388
 
389
  /* manage the capacity event */
390
  SUBTIMESPEC(&schedule_time, &cap_lasttime, &ty);
391
  tx = TIMESPEC2USEC(&ty);
393 giacomo 392
  if (proc_table[exec_shadow].control & CONTROL_CAP) proc_table[exec_shadow].avail_time -= tx;
38 pj 393
  jet_update_slice(tx);
394
 
395
  /* if the event didn't fire before, we delete it. */
396
  if (cap_timer != NIL) {
397
    kern_event_delete(cap_timer);
398
    cap_timer = NIL;
399
  }
400
}
401
 
402
/* This function is called by the kernel into kern.c to register a default
403
   exception handler */
404
int set_default_exception_handler(void);
567 giacomo 405
int remove_default_exception_handler(void);
38 pj 406
 
407
/*---------------------------------------------------------------------*/
2 pj 408
/* Task management primitives                                          */
409
/*---------------------------------------------------------------------*/
410
 
411
 
412
/*+ This primitive:
413
    - Reserve a task descriptor
414
    - Create the task based on the task model passed
415
    - Initialize the resources used by the task
416
    - Guarantees the task set
417
+*/
418
PID task_createn(char *name,      /*+ the symbolic name of the task +*/
419
                 TASK (*body)(),  /*+ a pointer to the task body    +*/
420
                 TASK_MODEL *m,   /*+ the task model                +*/
421
                                  /*+ the resources models, a list  +*/
422
                 ...);            /*+ of models terminated by NULL  +*/
423
 
424
 
425
/*+ a redefinition of task_createn +*/
426
extern __inline PID task_create(char *name, TASK (*body)(),
427
                                void *m, void *r)
428
{
429
   return task_createn(name, body, (TASK_MODEL *)m, (RES_MODEL *)r, NULL);
430
}
431
 
432
 
433
/* This function allow to create a set of tasks without guarantee.
434
   It must be called with interrupts disabled and it must be used with
435
   group_create_accept and group_create_reject.
436
 
437
   This function allocates a task descriptor and fills it.
438
   After that, the guarantee() function should be called to check for task(s)
439
   admission.
440
   Next, each task created with group_create must be accepted with a call to
441
   group_create_accept() or rejected with a call to group_create_reject.
442
 
443
   The function returns the PID of the allocated descriptor, or NIL(-1)
444
   if the descriptor cannot be allocated or some problems arises the creation.
445
   If -1 is returned, errno is set to a value that represent the error:
446
      ENO_AVAIL_TASK       -> no free descriptors available
447
      ENO_AVAIL_SCHEDLEVEL -> there were no scheduling modules that can handle
448
                              the TASK_MODEL *m passed as parameter
449
      ETASK_CREATE         -> there was an error during the creation of the
450
                              task into the scheduling module
451
      ENO_AVAIL_RESLEVEL   -> there were no resource modules that can handle
452
                              one of the RES_MODEL * passed as parameter
453
*/
454
PID group_create(char *name,
455
                 TASK (*body)(),
456
                 TASK_MODEL *m,
457
                 ...);
458
 
459
 
460
/*
461
  This function should be called when a task created with group_create
462
  is successfully guaranteed and accepted in the system.
463
  This function finish the creation process allocating the last resources
464
  for the task (i.e., the stack and the context).
465
  it returns:
466
 
467
  -1 if something goes wrong. In this case, THE TASK IS AUTOMATICALLY REJECTED
468
     AND THE GROUP_CREATE_REJECT MUST NOT BE CALLED.
469
     errno is set to a value that explains the problem occurred:
470
 
471
     ENO_AVAIL_STACK_MEM -> No stack memory available for the task
472
     ENO_AVAIL_TSS       -> No context available for the task (This is a
473
                            CRITICAL error, and usually never happens...)
474
*/
475
int group_create_accept(PID i, TASK_MODEL *m);
476
 
477
/*
478
  This function should be called when a task created with group_create
479
  can not be successfully guaranteed.
480
  This function reject the task from the system.
481
  You cannot use the PID of a rejected task after this call.
482
*/
483
void group_create_reject(PID i);
484
 
485
/*+
486
  It blocks all explicit activation of a task made with task_activate and
487
  group_activate. These activations are registered in an internal counter,
488
  returned by task_unblock_activation.
489
  it returns 0 if all ok, or -1 otherwise. errno is set accordingly.
490
+*/
491
int task_block_activation(PID p);
492
 
493
/*+
494
  It unblocks all explicit activations of a task, and returns the number of
495
  "frozen" activations. It not call the task_activate!!!!
496
  it returns -1 if an error occurs. errno is set accordingly.
497
+*/
498
int task_unblock_activation(PID p);
499
 
500
 
501
/*+ Activate a task specified via pid returned from task_create +*/
502
int task_activate(PID pid);
503
 
690 anton 504
/*+ Activate a task specified via pid from task_create at time t +*/
505
int task_activate_at(PID pid, struct timespec *t);
506
 
2 pj 507
/*+ Kill a task specified via pid returned from task_create +*/
508
int task_kill(PID pid);
509
 
510
/*+
511
  This primitive autokills the excuting task; it was used to avoid
512
  that returning from a task cause a jmp to an unpredictable location.
513
 
514
  Now it is obsolete, the task_create_stub do all the works.
515
 
516
  It is used by the Posix layer to implement pthread_exit
517
+*/
518
void task_abort(void *returnvalue);
519
 
520
/*+ Creates a cancellation point in the calling task +*/
521
void task_testcancel(void);
522
 
523
/*+ Set the cancellation state of the task +*/
524
int task_setcancelstate(int state, int *oldstate);
525
 
526
/*+ Set the cancellation type of the task +*/
527
int task_setcanceltype(int type, int *oldtype);
528
 
529
/*+ this function suspends execution of the calling task until the target
530
    task terminates, unless the target task has already terminated.
531
    It works like the pthread_join +*/
532
int task_join(PID p, void **value);
533
 
534
/*+ this function set the detach state of a task to joinable. This function
535
    is not present in Posix standard...
536
    returns ESRCH if p is non correct +*/
537
int task_joinable(PID p);
538
 
539
/*+ this function set the detach state of a task to detached. This function
540
    works as the posix's pthread_detach
541
    returns EINVAL if p can't be joined (or currently a task has done a
542
    join on it (condition not provided in posix)
543
    ESRCH if p is not correct +*/
544
int task_unjoinable(PID p);
545
 
546
/*+ Disable the preemption mechanism on the task.
547
    This primitive is very dangerous!!!!         +*/
548
void task_nopreempt(void);
549
 
550
/*+ Enable the preemption mechanism on the task. +*/
551
void task_preempt(void);
552
 
38 pj 553
/*+ sends a message to the scheduling module that is handling the task +*/
208 giacomo 554
int task_message(void *m, PID p, int reschedule);
38 pj 555
 
2 pj 556
/*+ This function signals to the kernel that the current istance of
557
    the task (periodic or aperiodic) is ended; so the task can be
558
    suspended until it is activated again. Pending activations may be saved
559
    depending on the task model +*/
38 pj 560
extern __inline__ void task_endcycle(void)
561
{
912 pj 562
  task_testcancel();
210 giacomo 563
  task_message(NULL, NIL, 1);
38 pj 564
}
2 pj 565
 
208 giacomo 566
/*+ This function signals to the kernel that the current istance of
567
    the task (periodic or aperiodic) is ended; so the task can be
568
    suspended until it is activated again. Pending activations may be saved
569
    depending on the task model +*/
570
extern __inline__ void task_disable(PID p)
571
{
572
  task_message((void *)(1), p, 0);
573
}
574
 
2 pj 575
/*+ This primitives refers the group id which is supplied
576
    by the application, not by the kernel                 +*/
577
int group_activate(WORD g);
690 anton 578
int group_activate_at(WORD g, struct timespec *t);
2 pj 579
int group_kill(WORD g);
580
 
581
 
582
/*---------------------------------------------------------------------*/
583
/* Mutex primitives                                                    */
584
/*---------------------------------------------------------------------*/
585
 
586
/* This primitives manages a mutex in the system.
587
   The behavior of the functions is similar to the POSIX ones. */
588
 
589
 
590
/*+ Alloc a mutex descriptor +*/
591
int mutex_init(mutex_t *mutex, const mutexattr_t *attr);
592
 
593
/*+ Free a mutex descriptor  +*/
594
int mutex_destroy(mutex_t *mutex);
595
 
596
/*+ Block wait               +*/
597
int mutex_lock(mutex_t *mutex);
598
 
599
/*+ Non-block wait           +*/
600
int mutex_trylock(mutex_t *mutex);
601
 
602
/*+ unlock primitive +*/
603
int mutex_unlock(mutex_t *mutex);
604
 
605
/*---------------------------------------------------------------------*/
606
/* Condition variables                                                 */
607
/*---------------------------------------------------------------------*/
608
 
609
/*+ Initialization of the condition variable +*/
610
int cond_init(cond_t *cond);
611
 
612
/*+ free a condition variable descriptor +*/
613
int cond_destroy(cond_t *cond);
614
 
615
/*+ signal on a condition variable, it unlocks only one task +*/
616
int cond_signal(cond_t *cond);
617
 
618
/*+ broadcast on a condition variable, it unlocks all the blocked tasks +*/
619
int cond_broadcast(cond_t *cond);
620
 
621
/*+ wait on a condition variable +*/
622
int cond_wait(cond_t *cond, mutex_t *mutex);
623
 
624
/*+ wait on a condition variable (with timer) +*/
625
int cond_timedwait(cond_t *cond, mutex_t *mutex,
626
                   const struct timespec *abstime);
627
 
628
/*---------------------------------------------------------------------*/
629
/* Task specific data primitives                                       */
630
/*---------------------------------------------------------------------*/
631
 
632
/* they are similar to the POSIX standard */
633
 
634
int task_key_create(task_key_t *key, void (*destructor)(void *));
635
void *task_getspecific(task_key_t key);
636
int task_setspecific(task_key_t key, const void *value);
637
int task_key_delete(task_key_t key);
638
 
639
/*---------------------------------------------------------------------*/
640
/* Task cancellation handlers                                          */
641
/*---------------------------------------------------------------------*/
642
 
643
/*+ push the specified cancellation cleanup handler routine onto
644
    the cancellation cleanup stack
645
void task_cleanup_push(void (*routine)(void *), void *arg); +*/
646
#define task_cleanup_push(rtn,arg) { \
647
        struct _task_handler_rec __cleanup_handler, **__head; \
648
        __cleanup_handler.f = rtn; \
649
        __cleanup_handler.a = arg; \
650
        __head = task_getspecific(0); \
651
        __cleanup_handler.next = *__head; \
652
        *__head = &__cleanup_handler;
653
 
654
/*+
655
    removes the routine at the top of the cancellation cleanup stack
656
    of the calling thread
657
void task_cleanup_pop(int execute); +*/
658
#define task_cleanup_pop(ex) \
659
        *__head = __cleanup_handler.next; \
660
        if (ex) (*__cleanup_handler.f) (__cleanup_handler.a); \
661
}
662
 
79 pj 663
__END_DECLS
2 pj 664
#endif /* __FUNC_H__ */