void mq_desblockalloc(void) { FAR struct mq_des_block_s *mqdesblock; /* Allocate a block of message descriptors */ mqdesblock = (FAR struct mq_des_block_s *)kmm_malloc(sizeof(struct mq_des_block_s)); if (mqdesblock) { int i; /* Add the block to the list of allocated blocks (in case * we ever need to reclaim the memory. */ sq_addlast((FAR sq_entry_t*)&mqdesblock->queue, &g_desalloc); /* Then add each message queue descriptor to the free list */ for (i = 0; i < NUM_MSG_DESCRIPTORS; i++) { sq_addlast((FAR sq_entry_t*)&mqdesblock->mqdes[i], &g_desfree); } } }
void mq_msgfree(FAR mqmsg_t *mqmsg) { irqstate_t saved_state; /* If this is a generally available pre-allocated message, * then just put it back in the free list. */ if (mqmsg->type == MQ_ALLOC_FIXED) { /* Make sure we avoid concurrent access to the free * list from interrupt handlers. */ saved_state = irqsave(); sq_addlast((FAR sq_entry_t*)mqmsg, &g_msgfree); irqrestore(saved_state); } /* If this is a message pre-allocated for interrupts, * then put it back in the correct free list. */ else if (mqmsg->type == MQ_ALLOC_IRQ) { /* Make sure we avoid concurrent access to the free * list from interrupt handlers. */ saved_state = irqsave(); sq_addlast((FAR sq_entry_t*)mqmsg, &g_msgfreeirq); irqrestore(saved_state); } /* Otherwise, deallocate it. Note: interrupt handlers * will never deallocate messages because they will not * received them. */ else if (mqmsg->type == MQ_ALLOC_DYN) { sched_kfree(mqmsg); } else { PANIC(); } }
static inline void timer_free(struct posix_timer_s *timer) { irqstate_t flags; /* Remove the timer from the allocated list */ flags = irqsave(); sq_rem((FAR sq_entry_t *)timer, (FAR sq_queue_t *)&g_alloctimers); /* Return it to the free list if it is one of the preallocated timers */ #if CONFIG_PREALLOC_TIMERS > 0 if ((timer->pt_flags & PT_FLAGS_PREALLOCATED) != 0) { sq_addlast((FAR sq_entry_t *)timer, (FAR sq_queue_t *)&g_freetimers); irqrestore(flags); } else #endif { /* Otherwise, return it to the heap */ irqrestore(flags); sched_kfree(timer); } }
static int enqueue_work_item_and_wait_for_result(work_q_item_t *item) { /* put the work item at the end of the work queue */ lock_queue(&g_work_q); sq_addlast(&item->link, &(g_work_q.q)); /* Adjust the queue size and potentially the maximum queue size */ if (++g_work_q.size > g_work_q.max_size) { g_work_q.max_size = g_work_q.size; } unlock_queue(&g_work_q); /* tell the work thread that work is available */ px4_sem_post(&g_work_queued_sema); /* wait for the result */ px4_sem_wait(&item->wait_sem); int result = item->result; destroy_work_item(item); return result; }
void wd_initialize(void) { /* Initialize the free watchdog list */ sq_init(&g_wdfreelist); /* The g_wdfreelist must be loaded at initialization time to hold the * configured number of watchdogs. */ g_wdpool = (FAR wdog_t*)kmalloc(sizeof(wdog_t) * CONFIG_PREALLOC_WDOGS); if (g_wdpool) { FAR wdog_t *wdog = g_wdpool; int i; for (i = 0; i < CONFIG_PREALLOC_WDOGS; i++) { sq_addlast((FAR sq_entry_t*)wdog++, &g_wdfreelist); } } /* The g_wdactivelist queue must be reset at initialization time. */ sq_init(&g_wdactivelist); }
static void vnc_add_queue(FAR struct vnc_session_s *session, FAR struct vnc_fbupdate_s *rect) { /* Lock the scheduler to assure that the sq_addlast() and the sem_post() * are atomic. */ sched_lock(); vnc_sem_debug(session, "Before add", 1); /* Put the entry into the list of queued rectangles. */ sq_addlast((FAR sq_entry_t *)rect, &session->updqueue); /* Post the semaphore to indicate the availability of one more rectangle * in the queue. This may wakeup the updater. */ sem_post(&session->queuesem); vnc_sem_debug(session, "After add", 0); DEBUGASSERT(session->queuesem.semcount <= CONFIG_VNCSERVER_NUPDATES); sched_unlock(); }
static struct mqueue_msg_s * mq_msgblockalloc(FAR sq_queue_t *queue, uint16_t nmsgs, uint8_t alloc_type) { struct mqueue_msg_s *mqmsgblock; /* The g_msgfree must be loaded at initialization time to hold the * configured number of messages. */ mqmsgblock = (FAR struct mqueue_msg_s*) kmm_malloc(sizeof(struct mqueue_msg_s) * nmsgs); if (mqmsgblock) { struct mqueue_msg_s *mqmsg = mqmsgblock; int i; for (i = 0; i < nmsgs; i++) { mqmsg->type = alloc_type; sq_addlast((FAR sq_entry_t*)mqmsg++, queue); } } return mqmsgblock; }
int net_addroute(uip_ipaddr_t target, uip_ipaddr_t netmask, uip_ipaddr_t router) { FAR struct net_route_s *route; uip_lock_t save; /* Allocate a route entry */ route = net_allocroute(); if (!route) { ndbg("ERROR: Failed to allocate a route\n"); return -ENOMEM; } /* Format the new route table entry */ uip_ipaddr_copy(route->target, target); uip_ipaddr_copy(route->netmask, netmask); uip_ipaddr_copy(route->router, router); /* Get exclusive address to the networking data structures */ save = uip_lock(); /* Then add the new entry to the table */ sq_addlast((FAR sq_entry_t *)route, (FAR sq_queue_t *)&g_routes); uip_unlock(save); return OK; }
int wd_delete(WDOG_ID wdog) { irqstate_t state; DEBUGASSERT(wdog); /* The following steps are atomic... the watchdog must not be active when * it is being deallocated. */ state = irqsave(); /* Check if the watchdog has been started. */ if (WDOG_ISACTIVE(wdog)) { /* Yes.. stop it */ wd_cancel(wdog); } /* Did this watchdog come from the pool of pre-allocated timers? Or, was * it allocated from the heap? */ if (WDOG_ISALLOCED(wdog)) { /* It was allocated from the heap. Use sched_kfree() to release the * memory. If the timer was released from an interrupt handler, * sched_kfree() will defer the actual deallocation of the memory * until a more appropriate time. * * We don't need interrupts disabled to do this. */ irqrestore(state); sched_kfree(wdog); } /* This was a pre-allocated timer. This function should not be called for * statically allocated timers. */ else if (!WDOG_ISSTATIC(wdog)) { /* Put the timer back on the free list and increment the count of free * timers, all with interrupts disabled. */ sq_addlast((FAR sq_entry_t *)wdog, &g_wdfreelist); g_wdnfree++; DEBUGASSERT(g_wdnfree <= CONFIG_PREALLOC_WDOGS); irqrestore(state); } /* Return success */ return OK; }
/** * @brief Put the node to the back of the queue. * * @param queue The target queue to put. * @param node The pointer to node. * @return None. */ static void put_node_back(sq_queue_t *queue, struct buf_node *node) { irqstate_t flags = irqsave(); sq_addlast(&node->entry, queue); irqrestore(flags); }
void sig_releasependingsignal(FAR sigpendq_t *sigpend) { irqstate_t saved_state; /* If this is a generally available pre-allocated structyre, * then just put it back in the free list. */ if (sigpend->type == SIG_ALLOC_FIXED) { /* Make sure we avoid concurrent access to the free * list from interrupt handlers. */ saved_state = irqsave(); sq_addlast((FAR sq_entry_t*)sigpend, &g_sigpendingsignal); irqrestore(saved_state); } /* If this is a message pre-allocated for interrupts, * then put it back in the correct free list. */ else if (sigpend->type == SIG_ALLOC_IRQ) { /* Make sure we avoid concurrent access to the free * list from interrupt handlers. */ saved_state = irqsave(); sq_addlast((FAR sq_entry_t*)sigpend, &g_sigpendingirqsignal); irqrestore(saved_state); } /* Otherwise, deallocate it. Note: interrupt handlers * will never deallocate signals because they will not * receive them. */ else if (sigpend->type == SIG_ALLOC_DYN) { sched_kfree(sigpend); } }
int uip_backlogdelete(FAR struct uip_conn *conn, FAR struct uip_conn *blconn) { FAR struct uip_backlog_s *bls; FAR struct uip_blcontainer_s *blc; FAR struct uip_blcontainer_s *prev; nllvdbg("conn=%p blconn=%p\n", conn, blconn); #ifdef CONFIG_DEBUG if (!conn) { return -EINVAL; } #endif bls = conn->backlog; if (bls) { /* Find the container hold the connection */ for (blc = (FAR struct uip_blcontainer_s *)sq_peek(&bls->bl_pending), prev = NULL; blc; prev = blc, blc = (FAR struct uip_blcontainer_s *)sq_next(&blc->bc_node)) { if (blc->bc_conn == blconn) { if (prev) { /* Remove the a container from the middle of the list of * pending connections */ (void)sq_remafter(&prev->bc_node, &bls->bl_pending); } else { /* Remove the a container from the head of the list of * pending connections */ (void)sq_remfirst(&bls->bl_pending); } /* Put container in the free list */ blc->bc_conn = NULL; sq_addlast(&blc->bc_node, &bls->bl_free); return OK; } } nlldbg("Failed to find pending connection\n"); return -EINVAL; } return OK; }
uint16_t uip_datahandler(FAR struct uip_conn *conn, FAR uint8_t *buffer, uint16_t buflen) { FAR struct uip_readahead_s *readahead1; FAR struct uip_readahead_s *readahead2 = NULL; uint16_t remaining; uint16_t recvlen = 0; /* First, we need to determine if we have space to buffer the data. This * needs to be verified before we actually begin buffering the data. We * will use any remaining space in the last allocated readahead buffer * plus as much one additional buffer. It is expected that the size of * readahead buffers are tuned so that one full packet will always fit * into one readahead buffer (for example if the buffer size is 420, then * a readahead buffer of 366 will hold a full packet of TCP data). */ readahead1 = (FAR struct uip_readahead_s*)conn->readahead.tail; if ((readahead1 && (CONFIG_NET_TCP_READAHEAD_BUFSIZE - readahead1->rh_nbytes) > buflen) || (readahead2 = uip_tcpreadahead_alloc()) != NULL) { /* We have buffer space. Now try to append add as much data as possible * to the last readahead buffer attached to this connection. */ remaining = buflen; if (readahead1) { recvlen = uip_readahead(readahead1, buffer, remaining); if (recvlen > 0) { buffer += recvlen; remaining -= recvlen; } } /* Do we need to buffer into the newly allocated buffer as well? */ if (readahead2) { readahead2->rh_nbytes = 0; recvlen += uip_readahead(readahead2, buffer, remaining); /* Save the readahead buffer in the connection structure where * it can be found with recv() is called. */ sq_addlast(&readahead2->rh_node, &conn->readahead); } } nllvdbg("Buffered %d bytes (of %d)\n", recvlen, buflen); return recvlen; }
void sched_ufree(FAR void *address) { #ifdef CONFIG_BUILD_KERNEL /* REVISIT: It is not safe to defer user allocation in the kernel mode * build. Why? Because the correct user context is in place now but * will not be in place when the deferred de-allocation is performed. In * order to make this work, we would need to do something like: (1) move * g_delayed_kufree into the group structure, then traverse the groups to * collect garbage on a group-by-group basis. */ ASSERT(!up_interrupt_context()); kumm_free(address); #else /* Check if this is an attempt to deallocate memory from an exception * handler. If this function is called from the IDLE task, then we * must have exclusive access to the memory manager to do this. */ if (up_interrupt_context() || kumm_trysemaphore() != 0) { irqstate_t flags; /* Yes.. Make sure that this is not a attempt to free kernel memory * using the user deallocator. */ flags = irqsave(); #if (defined(CONFIG_BUILD_PROTECTED) || defined(CONFIG_BUILD_KERNEL)) && \ defined(CONFIG_MM_KERNEL_HEAP) DEBUGASSERT(!kmm_heapmember(address)); #endif /* Delay the deallocation until a more appropriate time. */ sq_addlast((FAR sq_entry_t *)address, (FAR sq_queue_t *)&g_delayed_kufree); /* Signal the worker thread that is has some clean up to do */ #ifdef CONFIG_SCHED_WORKQUEUE work_signal(LPWORK); #endif irqrestore(flags); } else { /* No.. just deallocate the memory now. */ kumm_free(address); kumm_givesemaphore(); } #endif }
void sq_addafter(sq_entry_t *prev, sq_entry_t *node, sq_queue_t *queue) { if (!queue->head || prev == queue->tail) { sq_addlast(node, queue); } else { node->flink = prev->flink; prev->flink = node; } }
void usbstrg_wrcomplete(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req) { FAR struct usbstrg_dev_s *priv; FAR struct usbstrg_req_s *privreq; irqstate_t flags; /* Sanity check */ #ifdef CONFIG_DEBUG if (!ep || !ep->priv || !req || !req->priv) { usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_WRCOMPLETEINVALIDARGS), 0); return; } #endif /* Extract references to private data */ priv = (FAR struct usbstrg_dev_s*)ep->priv; privreq = (FAR struct usbstrg_req_s *)req->priv; /* Return the write request to the free list */ flags = irqsave(); sq_addlast((sq_entry_t*)privreq, &priv->wrreqlist); irqrestore(flags); /* Process the received data unless this is some unusual condition */ switch (req->result) { case OK: /* Normal completion */ usbtrace(TRACE_CLASSWRCOMPLETE, req->xfrd); break; case -ESHUTDOWN: /* Disconnection */ usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_WRSHUTDOWN), 0); break; default: /* Some other error occurred */ usbtrace(TRACE_CLSERROR(USBSTRG_TRACEERR_WRUNEXPECTED), (uint16_t)-req->result); break; }; /* Inform the worker thread that a write request has been returned */ priv->theventset |= USBSTRG_EVENT_WRCOMPLETE; pthread_cond_signal(&priv->cond); }
void tcp_wrbuffer_release(FAR struct tcp_wrbuffer_s *wrb) { DEBUGASSERT(wrb && wrb->wb_iob); /* To avoid deadlocks, we must following this ordering: Release the I/O * buffer chain first, then the write buffer structure. */ iob_free_chain(wrb->wb_iob); /* Then free the write buffer structure */ sq_addlast(&wrb->wb_node, &g_wrbuffer.freebuffers); sem_post(&g_wrbuffer.sem); }
void net_freeroute(FAR struct net_route_s *route) { uip_lock_t save; DEBUGASSERT(route); /* Get exclusive address to the networking data structures */ save = uip_lock(); /* Then add the new entry to the table */ sq_addlast((FAR sq_entry_t *)route, (FAR sq_queue_t *)&g_freeroutes); uip_unlock(save); }
static int sig_queueaction(FAR struct tcb_s *stcb, siginfo_t *info) { FAR sigactq_t *sigact; FAR sigq_t *sigq; irqstate_t flags; int ret = OK; sched_lock(); DEBUGASSERT(stcb != NULL && stcb->group != NULL); /* Find the group sigaction associated with this signal */ sigact = sig_findaction(stcb->group, info->si_signo); /* Check if a valid signal handler is available and if the signal is * unblocked. NOTE: There is no default action. */ if ((sigact) && (sigact->act.sa_u._sa_sigaction)) { /* Allocate a new element for the signal queue. NOTE: * sig_allocatependingsigaction will force a system crash if it is * unable to allocate memory for the signal data */ sigq = sig_allocatependingsigaction(); if (!sigq) { ret = -ENOMEM; } else { /* Populate the new signal queue element */ sigq->action.sighandler = sigact->act.sa_u._sa_sigaction; sigq->mask = sigact->act.sa_mask; memcpy(&sigq->info, info, sizeof(siginfo_t)); /* Put it at the end of the pending signals list */ flags = enter_critical_section(); sq_addlast((FAR sq_entry_t *)sigq, &(stcb->sigpendactionq)); leave_critical_section(flags); } } sched_unlock(); return ret; }
int pm_register(FAR struct pm_callback_s *callbacks) { int ret; DEBUGASSERT(callbacks); /* Add the new entry to the end of the list of registered callbacks */ ret = pm_lock(); if (ret == OK) { sq_addlast(&callbacks->entry, &g_pmglobals.registry); pm_unlock(); } return ret; }
void sig_allocateactionblock(void) { sigactq_t *sigact; int i; /* Allocate a block of signal actions */ g_sigactionalloc = (sigactq_t*)kmalloc((sizeof(sigactq_t)) * NUM_SIGNAL_ACTIONS); sigact = g_sigactionalloc; for (i = 0; i < NUM_SIGNAL_ACTIONS; i++) { sq_addlast((FAR sq_entry_t*)sigact++, &g_sigfreeaction); } }
void net_initroute(void) { int i; /* Initialize the routing table and the free list */ sq_init(&g_routes); sq_init(&g_freeroutes); /* All all of the pre-allocated routing table entries to a free list */ for (i = 0; i < CONFIG_NET_MAXROUTES; i++) { sq_addlast((FAR sq_entry_t *)&g_preallocroutes[i], (FAR sq_queue_t *)&g_freeroutes); } }
static struct posix_timer_s *timer_allocate(void) { FAR struct posix_timer_s *ret; irqstate_t flags; uint8_t pt_flags; /* Try to get a preallocated timer from the free list */ #if CONFIG_PREALLOC_TIMERS > 0 flags = irqsave(); ret = (FAR struct posix_timer_s *)sq_remfirst((FAR sq_queue_t *)&g_freetimers); irqrestore(flags); /* Did we get one? */ if (ret) { pt_flags = PT_FLAGS_PREALLOCATED; } else #endif { /* Allocate a new timer from the heap */ ret = (FAR struct posix_timer_s *)kmm_malloc(sizeof(struct posix_timer_s)); pt_flags = 0; } /* If we have a timer, then put it into the allocated timer list */ if (ret) { /* Initialize the timer structure */ memset(ret, 0, sizeof(struct posix_timer_s)); ret->pt_flags = pt_flags; /* And add it to the end of the list of allocated timers */ flags = irqsave(); sq_addlast((FAR sq_entry_t *)ret, (FAR sq_queue_t *)&g_alloctimers); irqrestore(flags); } return ret; }
void uip_grpfree(FAR struct uip_driver_s *dev, FAR struct igmp_group_s *group) { uip_lock_t flags; grplldbg("Free: %p flags: %02x\n", group, group->flags); /* Cancel the wdog */ flags = uip_lock(); wd_cancel(group->wdog); /* Remove the group structure from the group list in the device structure */ sq_rem((FAR sq_entry_t*)group, &dev->grplist); /* Destroy the wait semapore */ (void)sem_destroy(&group->sem); /* Destroy the wdog */ wd_delete(group->wdog); /* Then release the group structure resources. Check first if this is one * of the pre-allocated group structures that we will retain in a free list. */ #if CONFIG_PREALLOC_IGMPGROUPS > 0 if (IS_PREALLOCATED(group->flags)) { grplldbg("Put back on free list\n"); sq_addlast((FAR sq_entry_t*)group, &g_freelist); uip_unlock(flags); } else #endif { /* No.. deallocate the group structure. Use sched_free() just in case * this function is executing within an interrupt handler. */ uip_unlock(flags); grplldbg("Call sched_free()\n"); sched_free(group); } }
static int sig_queueaction(FAR _TCB *stcb, siginfo_t *info) { FAR sigactq_t *sigact; FAR sigq_t *sigq; irqstate_t saved_state; int ret = OK; sched_lock(); /* Find the sigaction associated with this signal */ sigact = sig_findaction(stcb, info->si_signo); /* Check if a valid signal handler is available and if the signal is * unblocked. NOTE: There is no default action. */ if ((sigact) && (sigact->act.sa_u._sa_sigaction)) { /* Allocate a new element for the signal queue. NOTE: sig_allocatependingsigaction * will force a system crash if it is unable to allocate memory for the * signal data */ sigq = sig_allocatependingsigaction(); if (!sigq) ret = ERROR; else { /* Populate the new signal queue element */ sigq->action.sighandler = sigact->act.sa_u._sa_sigaction; sigq->mask = sigact->act.sa_mask; memcpy(&sigq->info, info, sizeof(siginfo_t)); /* Put it at the end of the pending signals list */ saved_state = irqsave(); sq_addlast((FAR sq_entry_t*)sigq, &(stcb->sigpendactionq)); irqrestore(saved_state); } } sched_unlock(); return ret; }
static FAR sigpendq_t *sig_addpendingsignal(FAR struct tcb_s *stcb, FAR siginfo_t *info) { FAR struct task_group_s *group; FAR sigpendq_t *sigpend; irqstate_t flags; DEBUGASSERT(stcb != NULL && stcb->group != NULL); group = stcb->group; /* Check if the signal is already pending for the group */ sigpend = sig_findpendingsignal(group, info->si_signo); if (sigpend) { /* The signal is already pending... retain only one copy */ memcpy(&sigpend->info, info, sizeof(siginfo_t)); } /* No... There is nothing pending in the group for this signo */ else { /* Allocate a new pending signal entry */ sigpend = sig_allocatependingsignal(); if (sigpend) { /* Put the signal information into the allocated structure */ memcpy(&sigpend->info, info, sizeof(siginfo_t)); /* Add the structure to the group pending signal list */ flags = enter_critical_section(); sq_addlast((FAR sq_entry_t *)sigpend, &group->tg_sigpendingq); leave_critical_section(flags); } } return sigpend; }
static FAR sigpendq_t *sig_addpendingsignal(FAR struct tcb_s *stcb, FAR siginfo_t *info) { FAR struct task_group_s *group = stcb->group; FAR sigpendq_t *sigpend; irqstate_t saved_state; DEBUGASSERT(group); /* Check if the signal is already pending */ sigpend = sig_findpendingsignal(group, info->si_signo); if (sigpend) { /* The signal is already pending... retain only one copy */ memcpy(&sigpend->info, info, sizeof(siginfo_t)); } /* No... There is nothing pending for this signo */ else { /* Allocate a new pending signal entry */ sigpend = sig_allocatependingsignal(); if (sigpend) { /* Put the signal information into the allocated structure */ memcpy(&sigpend->info, info, sizeof(siginfo_t)); /* Add the structure to the pending signal list */ saved_state = irqsave(); sq_addlast((FAR sq_entry_t*)sigpend, &group->sigpendingq); irqrestore(saved_state); } } return sigpend; }
static sigq_t *sig_allocateblock(sq_queue_t *siglist, uint16_t nsigs, uint8_t sigtype) { sigq_t *sigqalloc; sigq_t *sigq; int i; /* Allocate a block of pending signal actions */ sigqalloc = (sigq_t*)kmalloc((sizeof(sigq_t)) * nsigs); sigq = sigqalloc; for (i = 0; i < nsigs; i++) { sigq->type = sigtype; sq_addlast((FAR sq_entry_t*)sigq++, siglist); } return sigqalloc; }
static sigpendq_t *sig_allocatependingsignalblock(sq_queue_t *siglist, uint16_t nsigs, uint8_t sigtype) { sigpendq_t *sigpendalloc; sigpendq_t *sigpend; int i; /* Allocate a block of pending signal structures */ sigpendalloc = (sigpendq_t*)kmalloc((sizeof(sigpendq_t)) * nsigs); sigpend = sigpendalloc; for (i = 0; i < nsigs; i++) { sigpend->type = sigtype; sq_addlast((FAR sq_entry_t*)sigpend++, siglist); } return sigpendalloc; }
void sched_ufree(FAR void *address) { irqstate_t flags; /* Check if this is an attempt to deallocate memory from an exception * handler. If this function is called from the IDLE task, then we * must have exclusive access to the memory manager to do this. */ if (up_interrupt_context() || kumm_trysemaphore() != 0) { /* Yes.. Make sure that this is not a attempt to free kernel memory * using the user deallocator. */ flags = irqsave(); #if (defined(CONFIG_BUILD_PROTECTED) || defined(CONFIG_BUILD_KERNEL)) && \ defined(CONFIG_MM_KERNEL_HEAP) DEBUGASSERT(!kmm_heapmember(address)); #endif /* Delay the deallocation until a more appropriate time. */ sq_addlast((FAR sq_entry_t*)address, (sq_queue_t*)&g_delayed_kufree); /* Signal the worker thread that is has some clean up to do */ #ifdef CONFIG_SCHED_WORKQUEUE work_signal(LPWORK); #endif irqrestore(flags); } else { /* No.. just deallocate the memory now. */ kumm_free(address); kumm_givesemaphore(); } }