/* file: MachUseful.h */ /* author: MLehr */ /* * Copyright (C) 1994 * by the Rector and Board of Visitors of * the University of Virginia * * for more information: * Sanghyuk Son * Department of Computer Science * School of Engineering and Applied Science * University of Virginia * Charlottesville, VA 22903 */ /* The definitions in MachUseful.h make prototyping RT-Mach programs easier. MachUseful.h contains: * #includes of all necessary RT-Mach headers, so the programmer doesn't have to hunt * macros to initialize RT-Mach data structures so the programmer doesn't have to remember the precise format of the data structures * macros to call RT-Mach functions check return values automatically especially useful for functions which don't have man pages no additional overhead These definitions worked with RT-Mach MK83g. */ #ifndef _MACH_USEFUL_H_ #define _MACH_USEFUL_H_ #include #include #include #include #include #include /* this header may cause problems for mkdep(1) */ #include #include #include #include #include #include #include #include #include /* prototypes to keep gcc's "-Wall" option happy */ extern int _flsbuf (unsigned char, void *); extern int fprintf (FILE *, const char *, ...); extern int task_by_pid (int); extern kern_return_t clock_get_port (host_t, mach_port_t *); extern kern_return_t clock_get_time (mach_port_t, timespec_t *); extern kern_return_t rt_mach_port_allocate (task_t, mach_port_right_t, rt_mach_port_attr_t *, mach_port_t *); extern kern_return_t rt_mach_port_associate (thread_t, mach_port_t, rt_priority_t); extern kern_return_t task_wire_code (host_priv_t, vm_task_t); extern kern_return_t task_wire_data (host_priv_t, vm_task_t, boolean_t); /* exit consistently when error is detected */ #define CRASH(hint) \ { \ fprintf (stderr, \ "fatal error on line %d in file \"%s\" (hint: %d)\n", \ __LINE__, \ __FILE__, \ hint); \ exit (-1); \ } #define MACH_CRASH(function,status) \ { \ mach_error (function, status); \ fprintf (stderr, \ "on line %d in file \"%s\"\n", \ __LINE__, \ __FILE__); \ exit (-2); \ } #define STDLIB_CRASH(function) \ { \ perror (function); \ fprintf (stderr, \ "on line %d in file \"%s\"\n", \ __LINE__, \ __FILE__); \ exit (-3); \ } /******************************/ /* clock and timer related functions */ #define NSEC_PER_USEC 1000 #define USEC_PER_MSEC 1000 #define MSEC_PER_SEC 1000 #define SEC_PER_MIN 60 #define MIN_PER_HOUR 60 #define HOUR_PER_DAY 24 #define NSEC_PER_MSEC (NSEC_PER_USEC * USEC_PER_MSEC) #define NSEC_PER_SEC (NSEC_PER_MSEC * MSEC_PER_SEC) #define USEC_PER_SEC (USEC_PER_MSEC * MSEC_PER_SEC) #define SEC_PER_HOUR (SEC_PER_MIN * MIN_PER_HOUR) #define SEC_PER_DAY (SEC_PER_HOUR * HOUR_PER_DAY) #define NSEC_TO_USEC(ns) ((ns) / NSEC_PER_USEC) #define NSEC_TO_MSEC(ns) ((ns) / NSEC_PER_MSEC) #define NSEC_TO_SEC(ns) ((ns) / NSEC_PER_SEC) #define SEC_TO_MIN(s) ((s) / SEC_PER_MIN) #define SEC_TO_HOUR(s) ((s) / SEC_PER_HOUR) #define SEC_TO_DAY(s) ((s) / SEC_PER_DAY) /* Below are several useful macros for manipulating time specifications (timespec_t)-- for other useful functions on time specifications, see rt/timespec.h. timespec_cvt_to_msec: returns the # of milliseconds represented by a timespec timespec_init_zero: sets a timespec to zero timespec_init_infinity: sets a timespec to max time timespec_init_usec: sets a timespec to equivalent usecs timespec_init_msec: sets a timespec to equivalent msecs timespec_init_sec: sets a timespec to equivalent secs timespec_print: prints a timespec in hh:mm:ss.mmm.uuu.nnn format timespec_sub: subtracts the second argument from the first, placing the results in the first timespec_sub_nsec: subtracts nsec from the first, placing the results in the first timespec_add_usec: adds usec to the first, placing the results in the first timespec_div: divides the first argument by the second, placing the results in the first */ #define timespec_cvt_to_msec(time) \ (MSEC_PER_SEC * (time).seconds + NSEC_TO_MSEC (time.nanoseconds)) #define timespec_init_zero(time) \ (time).seconds = (time).nanoseconds = 0 #define timespec_init_infinity(time) \ { \ (time).seconds = ~0x0; \ (time).nanoseconds = (NSEC_PER_SEC - 1); \ } #define timespec_init_usec(time,usec) \ { \ (time).seconds = (usec) / USEC_PER_SEC; \ (time).nanoseconds = ((usec) % USEC_PER_SEC) * NSEC_PER_USEC; \ } #define timespec_init_msec(time,msec) \ { \ (time).seconds = (msec) / MSEC_PER_SEC; \ (time).nanoseconds = ((msec) % MSEC_PER_SEC) * NSEC_PER_MSEC; \ } #define timespec_init_sec(time,sec) \ { \ (time).seconds = (sec); \ (time).nanoseconds = 0; \ } #define timespec_print(stream,time) \ fprintf ((stream), \ "%2.2d:%2.2d:%2.2d.%3.3d.%3.3d.%3.3d", \ SEC_TO_HOUR ((time).seconds) % HOUR_PER_DAY, \ SEC_TO_MIN ((time).seconds) % MIN_PER_HOUR, \ ((time).seconds % SEC_PER_MIN), \ NSEC_TO_MSEC ((time).nanoseconds) % MSEC_PER_SEC, \ NSEC_TO_USEC ((time).nanoseconds) % USEC_PER_MSEC, \ (time).nanoseconds % NSEC_PER_USEC) #ifdef timespec_sub #undef timespec_sub #define timespec_sub(result,subtrahend) \ if ((result).nanoseconds < (subtrahend).nanoseconds) \ { \ (result).seconds -= ((subtrahend).seconds + 1); \ (result).nanoseconds += \ (NSEC_PER_SEC - (subtrahend).nanoseconds); \ } \ else \ { \ (result).seconds -= (subtrahend).seconds; \ (result).nanoseconds -= (subtrahend).nanoseconds; \ } #endif #define timespec_sub_nsec(result,subtrahend) \ if ((result).nanoseconds < (subtrahend)) \ { \ (result).seconds--; \ (result).nanoseconds += \ (NSEC_PER_SEC - (subtrahend)); \ } \ else \ (result).nanoseconds -= (subtrahend) #define timespec_add_usec(result,addend) \ (result).nanoseconds += (addend) * NSEC_PER_USEC; \ if ((result).nanoseconds >= NSEC_PER_SEC) \ { \ (result).seconds++; \ (result).nanoseconds -= NSEC_PER_SEC; \ } #define timespec_div(result,divisor) \ { \ unsigned long temp__; \ temp__ = (result).seconds % (divisor); \ (result).seconds /= (divisor); \ (result).nanoseconds /= (divisor); \ (result).nanoseconds += temp__ * (NSEC_PER_SEC / (divisor)); \ } /******************************/ /* clock: mach_port_t status: kern_return_t */ #define OPEN_RT_CLOCK(clock,status) \ { \ status = clock_get_port (mach_host_self (), &(clock)); \ if (status != KERN_SUCCESS) \ MACH_CRASH ("clock_get_port", status); \ } /******************************/ /* clock: mach_port_t time: timespec_t status: kern_return_t */ #define READ_TIME(clock,time,status) \ { \ status = clock_get_time (clock, &(time)); \ if (status != KERN_SUCCESS) \ MACH_CRASH ("clock_get_time", status); \ } /******************************/ /* timer: mach_timer_t clock: mach_port_t status: kern_return_t */ #define CREATE_TIMER(timer,clock,status) \ status = timer_create (mach_task_self (), &timer, clock); \ if (status != KERN_SUCCESS) \ MACH_CRASH ("timer_create", status); /******************************/ /* timer: mach_timer_t flags: int (flags to be passed to timer_cancel--see timer_cancel (2) for details--use 0 for no flags) status: kern_return_t */ #define CANCEL_TIMER(timer,flags,status) \ status = timer_cancel (timer, flags); \ if (status != KERN_SUCCESS) \ MACH_CRASH ("timer_cancel", status); /******************************/ /* timer: mach_timer_t exp: timespec_t (time to wake up) flags: int (flags to be passed to timer_sleep-- see timer_sleep (2) for details) status: kern_return_t */ #define SLEEP_ON_TIMER(timer,exp,flags,status) \ status = timer_sleep (timer, exp, flags); \ if (status != KERN_SUCCESS) \ MACH_CRASH ("timer_sleep", status); /******************************/ /* attrs: rt_priority_data_t (see rt/rt_priority.h) deadln: timespec_t pd: timespec_t prio: int (scheduling priority) */ #define INIT_RT_PRIORITY_ATTR(attrs,deadln,pd,prio) \ (attrs).rt_priority = prio; \ (attrs).rt_period = pd; \ (attrs).rt_deadline = deadln; \ timespec_init_zero ((attrs).rt_start_time); \ timespec_init_zero ((attrs).rt_deadline_time); \ timespec_init_zero ((attrs).reserve_period); \ (attrs).reserved = FALSE /******************************/ /* IPC related functions */ /******************************/ /* msg: (user-defined type) (message to init) header: mach_msg_header_t descriptor: mach_msg_type_t id: int (message identifier--useful for debugging) */ #define INIT_MESSAGE_HEADER(msg,header,descriptor,id) \ (header).msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND, \ MACH_MSG_TYPE_COPY_SEND); \ (header).msgh_size = sizeof (msg); \ (header).msgh_remote_port = MACH_PORT_NULL; \ (header).msgh_local_port = MACH_PORT_NULL; \ (header).msgh_seqno = 0; \ (header).msgh_id = id; \ (descriptor).msgt_name = MACH_MSG_TYPE_BYTE; \ (descriptor).msgt_size = 8; \ (descriptor).msgt_number = sizeof (msg) - \ sizeof (mach_msg_header_t) - \ sizeof (mach_msg_type_t); \ (descriptor).msgt_inline = TRUE; \ (descriptor).msgt_longform = FALSE; \ (descriptor).msgt_deallocate = FALSE /******************************/ /* msg: (user-defined type) (message to init) header: rt_mach_msg_header_t descriptor: mach_msg_type_t id: int (message identifier--useful for debugging) deadln: timespec_t pd: timespec_t prio: int (scheduling priority) */ #define INIT_RT_MESSAGE_HEADER(msg,header,descriptor,id,deadln,pd,prio) \ (header).msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND, \ MACH_MSG_TYPE_COPY_SEND); \ (header).msgh_size = sizeof (msg); \ (header).msgh_remote_port = MACH_PORT_NULL; \ (header).msgh_local_port = MACH_PORT_NULL; \ (header).msgh_seqno = 0; \ (header).msgh_id = id; \ INIT_RT_PRIORITY_ATTR ((header).msgh_priority, deadln, pd, prio); \ (descriptor).msgt_name = MACH_MSG_TYPE_BYTE; \ (descriptor).msgt_size = 8; \ (descriptor).msgt_number = sizeof (msg) - \ sizeof (rt_mach_msg_header_t) - \ sizeof (mach_msg_type_t); \ (descriptor).msgt_inline = TRUE; \ (descriptor).msgt_longform = FALSE; \ (descriptor).msgt_deallocate = FALSE /******************************/ /* port: mach_port_t status: kern_return_t */ #define CREATE_PORT(port,status) \ status = mach_port_allocate (mach_task_self (), \ MACH_PORT_RIGHT_RECEIVE, \ &(port)); \ if (status != KERN_SUCCESS) \ MACH_CRASH ("mach_port_allocate", status); \ status = mach_port_insert_right (mach_task_self (), \ port, \ port, \ MACH_MSG_TYPE_MAKE_SEND); \ if (status != KERN_SUCCESS) \ MACH_CRASH ("mach_port_insert_right", status); /******************************/ /* attr: rt_mach_port_attr_t (see rt_ipc/rt_port.h) msgsiz: size of each msg in bytes nmsgs: # of buffers to allocate at port disptch: dispatch control policy (see rt/rt_policy.h) pinherit: inheritance policy (see rt/rt_policy.h) phandoff: handoff policy (see rt/rt_policy.h) */ #define INIT_RT_PORT_ATTR(attrs,msgsiz,nmsgs,disptch,pinherit,phandoff) \ (attrs).size = msgsiz; \ (attrs).nbufs = nmsgs; \ (attrs).policy.dispatch = disptch; \ (attrs).policy.prio_inherit = pinherit; \ (attrs).policy.prio_handoff = phandoff /* port: mach_port_t attrs: rt_mach_port_attr_t (see rt_ipc/rt_port.h) status: kern_return_t */ #define CREATE_RT_PORT(port,attrs,status) \ status = rt_mach_port_allocate (mach_task_self (), \ MACH_PORT_RIGHT_RECEIVE, \ &(attrs), \ &(port)); \ if (status != KERN_SUCCESS) \ MACH_CRASH ("rt_mach_port_allocate", status); \ status = mach_port_insert_right (mach_task_self (), \ port, \ port, \ MACH_MSG_TYPE_MAKE_SEND); \ if (status != KERN_SUCCESS) \ MACH_CRASH ("mach_port_insert_right", status); /******************************/ /* thread: thread_t port: mach_port_t prio: rt_priority_data_t status: kern_return_t */ #define RT_PORT_ASSOCIATE(thread,port,prio,status) \ status = rt_mach_port_associate (thread, port, &(prio)); \ if (status != KERN_SUCCESS) \ MACH_CRASH ("rt_mach_port_associate", status); /******************************/ /* port: mach_port_t header: mach_msg_header_t ssiz: size of message to send (in bytes) rsiz: max size of message to receive (in bytes) status: kern_return_t */ #define SEND_RECEIVE_MESSAGE(port,header,ssiz,rsiz,status) \ (header).msgh_local_port = port; \ (header).msgh_remote_port = port; \ status = mach_msg (&(header), \ MACH_SEND_MSG | MACH_RCV_MSG, \ ssiz, \ rsiz, \ MACH_MSG_TIMEOUT_NONE, \ MACH_PORT_NULL); \ if (status != KERN_SUCCESS) \ MACH_CRASH ("mach_msg (send/recv)", status) /******************************/ /* port: mach_port_t header: rt_mach_msg_header_t ssiz: size of message to send (in bytes) rsiz: max size of message to receive (in bytes) status: kern_return_t */ #define SEND_RECEIVE_RT_MESSAGE(port,header,ssiz,rsiz,status) \ (header).msgh_local_port = port; \ (header).msgh_remote_port = port; \ status = rt_mach_msg (&(header), \ MACH_SEND_MSG | MACH_RCV_MSG, \ ssiz, \ rsiz, \ MACH_MSG_TIMEOUT_NONE, \ MACH_PORT_NULL); \ if (status != KERN_SUCCESS) \ MACH_CRASH ("mach_msg (send/recv)", status) /******************************/ /* port: mach_port_t header: mach_msg_header_t status: kern_return_t */ #define RECEIVE_MESSAGE(port,header,status) \ (header).msgh_local_port = port; \ status = mach_msg (&(header), \ MACH_RCV_MSG, \ 0, \ (header).msgh_size, \ port, \ MACH_MSG_TIMEOUT_NONE, \ MACH_PORT_NULL); \ if (status != KERN_SUCCESS) \ MACH_CRASH ("mach_msg (receive)", status) /******************************/ /* port: mach_port_t header: rt_mach_msg_header_t status: kern_return_t */ #define RECEIVE_RT_MESSAGE(port,header,status) \ (header).msgh_local_port = port; \ status = rt_mach_msg (&(header), \ MACH_RCV_MSG, \ 0, \ (header).msgh_size, \ port, \ MACH_MSG_TIMEOUT_NONE, \ MACH_PORT_NULL); \ if (status != KERN_SUCCESS) \ MACH_CRASH ("rt_mach_msg (receive)", status) /******************************/ /* port: mach_port_t header: mach_msg_header_t status: kern_return_t */ #define SEND_MESSAGE(port,header,status) \ (header).msgh_remote_port = port; \ status = mach_msg (&(header), \ MACH_SEND_MSG, \ (header).msgh_size, \ 0, \ MACH_PORT_NULL, \ MACH_MSG_TIMEOUT_NONE, \ MACH_PORT_NULL); \ if (status != KERN_SUCCESS) \ MACH_CRASH ("mach_msg (send)", status) /******************************/ /* port: mach_port_t header: rt_mach_msg_header_t status: kern_return_t */ #define SEND_RT_MESSAGE(port,header,status) \ (header).msgh_remote_port = port; \ status = rt_mach_msg (&(header), \ MACH_SEND_MSG, \ (header).msgh_size, \ 0, \ MACH_PORT_NULL, \ MACH_MSG_TIMEOUT_NONE, \ MACH_PORT_NULL); \ if (status != KERN_SUCCESS) \ MACH_CRASH ("rt_mach_msg (send)", status) /******************************/ /* synchronization related functions */ /******************************/ /* attrs: rt_mutex_attr_data_t (see rt/rt_sync_attr.h) polcy: priority control policy (see rt/rt_policy.h) deadln,pd: timespec_t (deadline, period) prio: unsigned int (scheduling priority) */ #define INIT_RT_MUTEX_ATTR(attrs,polcy,deadln,pd,prio) \ (attrs).mutex_policy = polcy; \ INIT_RT_PRIORITY_ATTR ((attrs).mutex_priority, deadln, pd, prio) /* mutex: rt_mutex_t attrs: rt_mutex_attr_data_t status: kern_return_t */ #define CREATE_MUTEX(mutex,attrs,status) \ status = rt_mutex_allocate (mach_host_self (), &(mutex), &(attrs)); \ if (status != KERN_SUCCESS) \ MACH_CRASH ("rt_mutex_allocate", status) /* mutex: rt_mutex_t status: kern_return_t */ #define DESTROY_MUTEX(mutex,status) \ status = rt_mutex_deallocate (mutex); \ if (status != KERN_SUCCESS) \ MACH_CRASH ("rt_mutex_deallocate", status) /******************************/ /* mutex: rt_mutex_t exp: timespec_t * (max time to wait to acquire) status: kern_return_t */ #define LOCK_MUTEX(mutex,exp,status) \ status = rt_mutex_lock (mutex, exp); \ if (status != KERN_SUCCESS) \ MACH_CRASH ("rt_mutex_lock", status) /******************************/ /* mutex: rt_mutex_t status: kern_return_t */ #define UNLOCK_MUTEX(mutex,status) \ status = rt_mutex_unlock (mutex); \ if (status != KERN_SUCCESS) \ MACH_CRASH ("rt_mutex_unlock", status) /******************************/ /* attrs: rt_cond_attr_data_t (see rt/rt_sync_attr.h) polcy: priority control policy (see rt/rt_policy.h) deadln,pd: timespec_t (deadline, period) prio: unsigned int (scheduling priority) */ #define INIT_RT_CONDITION_ATTR(attrs,polcy,deadln,pd,prio) \ (attrs).cond_policy = polcy; \ INIT_RT_PRIORITY_ATTR ((attrs).cond_priority, deadln, pd, prio) /* cond: rt_condition_t attrs: rt_cond_attr_data_t status: kern_return_t */ #define CREATE_CONDITION(cond,attrs,status) \ status = rt_condition_allocate (mach_host_self (), &(cond), &(attrs)); \ if (status != KERN_SUCCESS) \ MACH_CRASH ("rt_condition_allocate", status); /******************************/ /* cond: rt_condition_t status: kern_return_t */ #define SIGNAL_CONDITION(cond,status) \ puts ("rt_condition_signal"); \ status = rt_condition_signal (cond); \ if (status != KERN_SUCCESS) \ MACH_CRASH ("rt_condition_signal", status); /******************************/ /* cond: rt_condition_t mutex: rt_mutex_t (mutex to release before waiting) exp: timespec_t * (max time to wait to acquire) status: kern_return_t */ #define WAIT_CONDITION(cond,mutex,exp,status) \ puts ("rt_condition_wait"); \ status = rt_condition_wait (cond, mutex, exp); \ if (status != KERN_SUCCESS) \ MACH_CRASH ("rt_condition_wait", status); /******************************/ /* attrs: rt_event_attr_data_t (see rt/rt_sync_attr.h) polcy: priority control policy (see rt/rt_policy.h) deadln,pd: timespec_t (deadline, period) prio: unsigned int (scheduling priority) */ #define INIT_RT_EVENT_ATTR(attrs,polcy,deadln,pd,prio) \ (attrs).event_policy = polcy; \ INIT_RT_PRIORITY_ATTR ((attrs).event_priority, deadln, pd, prio) #define CREATE_EVENT(event,attrs,status) \ status = rt_event_allocate (&(event), &(attrs)); \ if (status != KERN_SUCCESS) \ MACH_CRASH ("rt_event_allocate", status); /******************************/ #define SIGNAL_EVENT(event,status) \ status = rt_event_signal (event); \ if (status != KERN_SUCCESS) \ MACH_CRASH ("rt_event_signal", status); /******************************/ /* exp: timespec_t * (max time to wait to acquire) */ #define WAIT_EVENT(event,thread,exp,status) \ status = rt_event_wait (event, thread, exp); \ if (status != KERN_SUCCESS) \ MACH_CRASH ("rt_event_wait", status); /******************************/ /* thread related functions */ /******************************/ /* attrs: rt_thread_attr_data_t (see rt/rt_thread.h) deadln,pd: timespec_t (deadline and period) abs: boolean_t (absolute or relative timespecs) entry: void (*f) (int *) (entry function) args: int * (arguments to pass to entry) stksiz: long (bytes to allocate for activation stack) status: kern_return_t */ #define INIT_RT_THREAD_ATTR(attrs,deadln,pd,prio,abs,entry,args,stksiz,status) \ (attrs).priority = prio; \ (attrs).period = pd; \ (attrs).deadline = deadln; \ (attrs).isperiodic = timespec_nonzero (pd); \ timespec_init_zero ((attrs).start_time); \ (attrs).abs_flag = abs; \ (attrs).deadline_port = MACH_PORT_NULL; \ (attrs).entrypoint = entry; \ (attrs).arg = args; \ (attrs).stack_size = stksiz; \ status = vm_allocate (mach_task_self (), \ &(attrs).stack_addr, \ stksiz, \ TRUE); \ if (status != KERN_SUCCESS) \ MACH_CRASH ("vm_allocate", status); \ status = vm_wire (mach_host_priv_self (), \ mach_task_self (), \ (attrs).stack_addr, \ stksiz, \ VM_PROT_DEFAULT); \ if (status != KERN_SUCCESS) \ MACH_CRASH ("vm_wire", status); /******************************/ #define CREATE_RT_THREAD(thread,attrs,status) \ status = rt_thread_create (mach_task_self (), \ &(thread), \ &(attrs)); \ if (status != KERN_SUCCESS) \ MACH_CRASH ("rt_thread_create", status); /******************************/ /* attrs: struct thread_sched_basic_attr (see rt/thread_attributes.h) */ #define GET_THREAD_ATTR(thread,attrs,size,status) \ size = THREAD_SCHED_BASIC_ATTR_COUNT; \ status = thread_get_attribute (thread, \ THREAD_SCHED_BASIC_ATTR, \ (thread_attr_t) &(attrs), \ &(size)); \ if (status != KERN_SUCCESS) \ MACH_CRASH ("thread_get_attribute", status) /******************************/ /* attrs: struct thread_sched_basic_attr (see rt/thread_attributes.h) */ #define SET_THREAD_ATTR(thread,attrs,prio,pd,deadln,status) \ (attrs).priority = prio; \ (attrs).period = pd; \ (attrs).deadline = deadln; \ status = thread_set_attribute (thread, \ THREAD_SCHED_BASIC_ATTR, \ (thread_attr_t) &(attrs), \ THREAD_SCHED_BASIC_ATTR_COUNT); \ if (status != KERN_SUCCESS) \ MACH_CRASH ("thread_set_attribute", status) /**********************************************************************/ /* thrd: thread_t status: kern_return_t */ #define RESUME_THREAD(thrd,status) \ status = thread_resume (thrd); \ if (status != KERN_SUCCESS) \ MACH_CRASH ("thread_set_attribute", status) /**********************************************************************/ /* thrd: thread_t status: kern_return_t */ #define SUSPEND_THREAD(thrd,status) \ status = thread_suspend (thrd); \ if (status != KERN_SUCCESS) \ MACH_CRASH ("thread_set_attribute", status) #endif /* _MACH_USEFUL_H_ */