示例#1
0
int hal_ring_detach(const char *name, ringbuffer_t *rbptr)
{

    CHECK_HALDATA();
    CHECK_STRLEN(name, HAL_NAME_LEN);
    CHECK_LOCK(HAL_LOCK_CONFIG);

    if ((rbptr == NULL) || (rbptr->magic != RINGBUFFER_MAGIC)) {
	HALERR("ring '%s': invalid ringbuffer", name);
	return -EINVAL;
    }

    // no mutex(es) held up to here
    {
	int retval __attribute__((cleanup(halpr_autorelease_mutex)));
	rtapi_mutex_get(&(hal_data->mutex));

	ringheader_t *rhptr = rbptr->header;
	rhptr->refcount--;
	rbptr->magic = 0;  // invalidate FIXME

	// unlocking happens automatically on scope exit
    }
    return 0;
}
示例#2
0
void timer_manager_t::register_timer(uint32_t                       interval_,
                                     const time_event_callback_t&   callback_,
                                     bool                           persist_,
                                     time_t                         start_time_
                                    )
{
    CHECK_LOCK(m_is_lock, m_mutex);

    time_t local_start_time = start_time_;

    if (!local_start_time)
    {
        local_start_time = get_cached_time_i().tv_sec;
    }

    time_event_t* time_event = new time_event_t(
                                                        local_start_time,
                                                        interval_,
                                                        0,
                                                        local_start_time + interval_,
                                                        callback_,
                                                        persist_
                                                    );
    if (NULL == time_event)
    {
        LOGWARN((TIMER_MANAGER_MODULE, "timer_manager_t::register_timer new time_event_t failed"));
        return;
    }

    //! yunjie: 这里不需要加锁, m_time_heap被初始化为线程安全的
    register_timer_i(time_event);
}
示例#3
0
文件: hal_funct.c 项目: ArcEye/MK-Qt5
int hal_del_funct_from_thread(const char *funct_name, const char *thread_name)
{
    hal_funct_t *funct;
    hal_list_t *list_root, *list_entry;
    hal_funct_entry_t *funct_entry;

    CHECK_HALDATA();
    CHECK_LOCK(HAL_LOCK_CONFIG);
    CHECK_STR(funct_name);
    CHECK_STR(thread_name);

    HALDBG("removing function '%s' from thread '%s'", funct_name, thread_name);
    {
	hal_thread_t *thread __attribute__((cleanup(halpr_autorelease_mutex)));

	/* get mutex before accessing data structures */
	rtapi_mutex_get(&(hal_data->mutex));

	/* search function list for the function */
	funct = halpr_find_funct_by_name(funct_name);
	if (funct == 0) {
	    HALERR("function '%s' not found", funct_name);
	    return -EINVAL;
	}
	/* found the function, is it in use? */
	if (funct->users == 0) {
	    HALERR("function '%s' is not in use", funct_name);
	    return -EINVAL;
	}
	/* search thread list for thread_name */
	thread = halpr_find_thread_by_name(thread_name);
	if (thread == 0) {
	    /* thread not found */
	    HALERR("thread '%s' not found", thread_name);
	    return -EINVAL;
	}
	/* ok, we have thread and function, does thread use funct? */
	list_root = &(thread->funct_list);
	list_entry = list_next(list_root);
	while (1) {
	    if (list_entry == list_root) {
		/* reached end of list, funct not found */
		HALERR("thread '%s' doesn't use %s",
		       thread_name, funct_name);
		return -EINVAL;
	    }
	    funct_entry = (hal_funct_entry_t *) list_entry;
	    if (SHMPTR(funct_entry->funct_ptr) == funct) {
		/* this funct entry points to our funct, unlink */
		list_remove_entry(list_entry);
		/* and delete it */
		free_funct_entry_struct(funct_entry);
		/* done */
		return 0;
	    }
	    /* try next one */
	    list_entry = list_next(list_entry);
	}
    }
}
示例#4
0
struct timeval timer_manager_t::get_cached_time()
{
    CHECK_LOCK(m_is_lock, m_mutex);

    struct timeval ret = get_cached_time_i();

    return ret;
}
int io_multiplex_handler_t::remove_fd_from_epoll(fd_t fd_)
{
    CHECK_LOCK(m_is_lock, m_mutex);

    int ret = remove_fd_from_epoll_i(fd_);

    return ret;
}
int io_multiplex_handler_t::register_io_event(fd_t fd_, int event_type_flag_, callback_on_event_t event_cb_, void* cb_arg_, bool is_persist_)
{
    CHECK_LOCK(m_is_lock, m_mutex);

    int ret = register_io_event_i(fd_, event_type_flag_, event_cb_, cb_arg_, is_persist_);

    return ret;
}
示例#7
0
void _dbg_spin_unlock(spinlock_t * lock, const char *base_file, int line_no)
{
	CHECK_LOCK(lock);
	volatile unsigned int *a;
	mb();
	a = __ldcw_align(lock);
	if (unlikely((*a != 0) && lock->babble)) {
		lock->babble--;
		pdc_printf(
			"%s:%d: spin_unlock(%s:%p) not locked\n",
			base_file, line_no, lock->module, lock);
	}
	*a = 1;	
	mb();
}
示例#8
0
void timer_manager_t::exec()
{
    CHECK_LOCK(m_is_lock, m_mutex);

    if (!m_inited)
    {
        LOGWARN((TIMER_MANAGER_MODULE, "timer_manager_t::exec timer manager has not been inited."));
        return;
    }

    time_t now = get_cached_time_i().tv_sec;

    timer_container_t::container_t tasks;
    uint32_t all_task_num = 0;

    //! yunjie: 用于在fetch_task过程中检测time event是否满足执行条件
    struct timer_container_t::cond_checker_t checker(time_event_fetch_condition, (void*)&now, FETCH_BREAK);

    //! yunjie: 一次性抓取足够的需要被执行的time事件.
    m_time_heap.fetch_task(tasks, all_task_num, 0xffffffff, &checker);

    while (!tasks.empty())
    {
        struct time_event_t* event = tasks.top();
        event->exec();

        if (event->persist)
        {
            event->last_exec_time = now;
            event->timestamp = now + event->interval;

            //! yunjie: 这时注册的event在该loop中将不会被检测.
            register_timer_i(event);
        }
        else
        {
            //! yunjie: release会对内部的async_method_t进行内存释放, 不是persist事件才能调用
            event->release();
            SAFE_DELETE(event);
        }

        tasks.pop();
    }
}
示例#9
0
uint32_t timer_manager_t::size()
{
    CHECK_LOCK(m_is_lock, m_mutex);

    return m_time_heap.size();
}
示例#10
0
void timer_manager_t::flush_time()
{
    CHECK_LOCK(m_is_lock, m_mutex);

    flush_time_i();
}
示例#11
0
void timer_manager_t::clear()
{
    CHECK_LOCK(m_is_lock, m_mutex);

    clear_i();
}
示例#12
0
int io_multiplex_handler_t::wait_io_notification()
{
    CHECK_LOCK(m_is_lock, m_mutex);

    if (0 == m_fd_set.size())   //! yunjie: 避免没有监听的fd也要system call陷入内核
    {
        return 0;
    }

    int timeout = 50;        //! yunjie: timeout暂时为50, 以后会进行优化
    int res = ::epoll_wait(m_epoll_fd, &m_epoll_events[0], m_epoll_events.size(), timeout);

    //! yunjie: epoll_wait 非正常返回值处理.
    if (res == -1)
    {
        LOGWARN((IO_MULTIPLEX_MODULE, "io_multiplex_handler_t::wait_notification epoll_wait failed, res:[%d]", res));
        return (0);
    }

    for (int i = 0; i < res; i++) 
    {
        int what = m_epoll_events[i].events;                        //! yunjie: 当前循环epoll事件类型
        fd_t fd = m_epoll_events[i].data.fd;                        //! yunjie: 当前循环epoll需要处理的fd

        if (fd < 0 || fd >= m_io_events.size())                     //! yunjie: 非正常边界
        {
            LOGWARN((IO_MULTIPLEX_MODULE, "io_multiplex_handler_t::wait_notification invaild file descriptor fd:[%d]", fd));
            continue;
        }

        struct io_event_t& io_event = m_io_events[fd];          //! yunjie: 通过索引的方式取得该fd所对应的io_event_t对象

        struct epoll_event epev = {0, {0}};
        epev.data.fd = io_event.listen_fd;

        /* yunjie: EPOLLPRI - 表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来).
         *         EPOLLERR - 表示对应的文件描述符发生错误.
         *         EPOLLHUP - 表示对应的文件描述符被挂断.
         */
        if (what & (EPOLLHUP|EPOLLERR))                         //! yunjie: 对于该fd的错误现象进行处理, 进行回调.
        {
            //! yunjie: 从epoll中删除关联的socket
            /** yunjie: 删除操作交给使用者去决定
            if (remove_fd_from_epoll_i(io_event.listen_fd) == -1)
            {
                LOGWARN((IO_MULTIPLEX_MODULE, "io_multiplex_handler_t::wait_notification remove_fd_from_epoll_i failed fd:%d.", io_event.listen_fd));
                continue;
            }
            */

            if (NULL != io_event.error_cb)
            {
                io_event.error_cb(fd, IO_ERROR_EVENT, io_event.error_cb_arg);
            }
        }
        else
        {
            int epoll_operate = 0;
            if (what & EPOLLIN)                                 //! yunjie: 读事件
            {
                if (NULL != io_event.read_cb)
                {
                    io_event.read_cb(fd, IO_READ_EVENT, io_event.read_cb_arg);       //! yunjie: 调用读回调
                }

                //! yunjie: 如果读事件不是persist的, 需要进行epoll信息的修改
                //! yunjie: 注意, 此时的io_event有可能已经被callback clear, 所以要来判断fd是否不为0
                if (io_event.listen_fd && !io_event.read_persist)
                {
                    //! yunjie: 如果有注册写事件, 那么不能直接删除epoll和socket的关联, 需要修改epoll信息, 否则就直接从epoll中删除socket的关联
                    if (NULL != io_event.write_cb)
                    {
                        epoll_operate = EPOLL_CTL_MOD;
                        epev.events = EPOLLOUT;
                    
                        if (::epoll_ctl(m_epoll_fd, epoll_operate, io_event.listen_fd, &epev) == -1)
                        {
                            LOGWARN((IO_MULTIPLEX_MODULE, "io_multiplex_handler_t::wait_notification system call epoll_ctl failed fd:%d.", io_event.listen_fd));
                            continue;
                        }
                    }
                    else
                    {
                        if (remove_fd_from_epoll_i(io_event.listen_fd) == -1)
                        {
                            LOGWARN((IO_MULTIPLEX_MODULE, "io_multiplex_handler_t::wait_notification remove_fd_from_epoll_i failed fd:%d.", io_event.listen_fd));
                            continue;
                        }
                    }

                    io_event.read_cb = NULL;
                    io_event.read_cb_arg = NULL;
                    io_event.read_persist = false;
                }
            }

            if (what & EPOLLOUT)                                //! yunjie: 写事件
            {                      
                if (NULL != io_event.write_cb)
                {
                    io_event.write_cb(fd, IO_WRITE_EVENT, io_event.write_cb_arg);     //! yunjie: 调用写回调
                }

                //! yunjie: 如果写事件不是persist的, 需要进行epoll信息的修改
                //! yunjie: 注意, 此时的io_event有可能已经被callback clear, 所以要来判断fd是否不为0
                if (io_event.listen_fd && !io_event.write_persist)
                {
                    //! yunjie: 如果有注册读事件, 那么不能直接删除epoll和socket的关联, 需要修改epoll信息, 否则就直接从epoll中删除socket的关联
                    if (NULL != io_event.read_cb)
                    {
                        epoll_operate = EPOLL_CTL_MOD;
                        epev.events = EPOLLIN;

                        if (::epoll_ctl(m_epoll_fd, epoll_operate, io_event.listen_fd, &epev) == -1)
                        {
                            LOGWARN((IO_MULTIPLEX_MODULE, "io_multiplex_handler_t::wait_notification system call epoll_ctl failed fd:%d.", io_event.listen_fd));
                            continue;
                        }
                    }
                    else
                    {
                        if (remove_fd_from_epoll_i(io_event.listen_fd) == -1)
                        {
                            LOGWARN((IO_MULTIPLEX_MODULE, "io_multiplex_handler_t::wait_notification remove_fd_from_epoll_i failed fd:%d.", io_event.listen_fd));
                            continue;
                        }
                    }

                    io_event.write_cb = NULL;
                    io_event.write_cb_arg = NULL;
                    io_event.write_persist = false;
                }
            }
        }
    }

    //! yunjie: 对m_epoll_events进行扩张, 这样的话可以尽可能通过一次epoll_wait我们能获得更多的fd, 减少内核线程的切换, 提高性能.
    int epoll_events_size = m_epoll_events.size();
    if (res == epoll_events_size && epoll_events_size < EPOLL_EVENT_WAIT_NUM_MAX) 
    {
        uint32_t new_size = epoll_events_size << 1;
        m_epoll_events.resize(new_size);      //! yunjie: 成倍扩张, 由于初始值对齐到8(32), 所以最终极限扩张值必定是EPOLL_EVENT_WAIT_NUM_MAX(4096)
        LOGWARN((IO_MULTIPLEX_MODULE, "io_multiplex_handler_t::wait_io_notification m_epoll_events resize to %lu.", new_size));
    }

    return res;
}
示例#13
0
int hal_ring_new(const char *name, int size, int sp_size, int mode)
{
    hal_ring_t *rbdesc;
    int *prev, next, cmp, retval;
    int ring_id;
    ringheader_t *rhptr;

    CHECK_HALDATA();
    CHECK_STRLEN(name, HAL_NAME_LEN);
    CHECK_LOCK(HAL_LOCK_LOAD);

    {
	hal_ring_t *ptr __attribute__((cleanup(halpr_autorelease_mutex)));

	rtapi_mutex_get(&(hal_data->mutex));

	// make sure no such ring name already exists
	if ((ptr = halpr_find_ring_by_name(name)) != 0) {
	    HALERR("ring '%s' already exists", name);
	    return -EEXIST;
	}
	// allocate a new ring id - needed since we dont track ring shm
	// segments in RTAPI
	if ((ring_id = next_ring_id()) < 0) {
	    HALERR("cant allocate new ring id for '%s'", name);
	    return -ENOMEM;
	}

	// allocate a new ring descriptor
	if ((rbdesc = alloc_ring_struct()) == 0)
	    NOMEM("ring '%s'", name);

	rbdesc->handle = rtapi_next_handle();
	rbdesc->flags = mode;
	rbdesc->ring_id = ring_id;

	// make total allocation fit ringheader, ringbuffer and scratchpad
	rbdesc->total_size = ring_memsize( rbdesc->flags, size, sp_size);

	if (rbdesc->flags & ALLOC_HALMEM) {
	    void *ringmem = shmalloc_up(rbdesc->total_size);
	    if (ringmem == NULL)
		NOMEM("ring '%s' size %d - insufficient HAL memory for ring",
		      name,rbdesc->total_size);

	    rbdesc->ring_offset = SHMOFF(ringmem);
	    rhptr = ringmem;
	} else {
	    // allocate shared memory segment for ring and init
	    rbdesc->ring_shmkey = OS_KEY((RTAPI_RING_SHM_KEY + ring_id), rtapi_instance);

	    int shmid;

	    // allocate an RTAPI shm segment owned by HAL_LIB_xxx
	    if ((shmid = rtapi_shmem_new(rbdesc->ring_shmkey, lib_module_id,
					 rbdesc->total_size)) < 0)
		NOMEM("rtapi_shmem_new(0x%8.8x,%d) failed: %d",
		       rbdesc->ring_shmkey, lib_module_id,
		       rbdesc->total_size);

	    // map the segment now so we can fill in the ringheader details
	    if ((retval = rtapi_shmem_getptr(shmid,
					     (void **)&rhptr, 0)) < 0)
		NOMEM("rtapi_shmem_getptr for %d failed %d",
		       shmid, retval);
	}

	HALDBG("created ring '%s' in %s, total_size=%d",
	       name, (rbdesc->flags & ALLOC_HALMEM) ? "halmem" : "shm",
	       rbdesc->total_size);

	ringheader_init(rhptr, rbdesc->flags, size, sp_size);
	rhptr->refcount = 0; // on hal_ring_attach: increase; on hal_ring_detach: decrease
	rtapi_snprintf(rbdesc->name, sizeof(rbdesc->name), "%s", name);
	rbdesc->next_ptr = 0;

	// search list for 'name' and insert new structure
	prev = &(hal_data->ring_list_ptr);
	next = *prev;
	while (1) {
	    if (next == 0) {
		/* reached end of list, insert here */
		rbdesc->next_ptr = next;
		*prev = SHMOFF(rbdesc);
		return 0;
	    }
	    ptr = SHMPTR(next);
	    cmp = strcmp(ptr->name, rbdesc->name);
	    if (cmp > 0) {
		/* found the right place for it, insert here */
		rbdesc->next_ptr = next;
		*prev = SHMOFF(rbdesc);
		return 0;
	    }
	    /* didn't find it yet, look at next one */
	    prev = &(ptr->next_ptr);
	    next = *prev;
	}
	// automatic unlock by scope exit
    }
}
示例#14
0
int hal_ring_delete(const char *name)
{
    int retval;

    CHECK_HALDATA();
    CHECK_STRLEN(name, HAL_NAME_LEN);
    CHECK_LOCK(HAL_LOCK_LOAD);

    {
	hal_ring_t *hrptr __attribute__((cleanup(halpr_autorelease_mutex)));
	rtapi_mutex_get(&(hal_data->mutex));

	// ring must exist
	if ((hrptr = halpr_find_ring_by_name(name)) == NULL) {
	    HALERR("ring '%s' not found", name);
	    return -ENOENT;
	}

	ringheader_t *rhptr;
	int shmid = -1;

	if (hrptr->flags & ALLOC_HALMEM) {
	    // ring exists as HAL memory.
	    rhptr = SHMPTR(hrptr->ring_offset);
	} else {
	    // ring exists as shm segment. Retrieve shared memory address.
	    if ((shmid = rtapi_shmem_new_inst(hrptr->ring_shmkey,
					      rtapi_instance, lib_module_id,
					      0 )) < 0) {
		if (shmid != -EEXIST)  {
		    HALERR("ring '%s': rtapi_shmem_new_inst() failed %d",
			   name, shmid);
		    return shmid;
		}
	    }
	    if ((retval = rtapi_shmem_getptr(shmid, (void **)&rhptr, 0))) {
		HALERR("ring '%s': rtapi_shmem_getptr %d failed %d",
		       name, shmid, retval);
		return -ENOMEM;
	    }
	}
	// assure attach/detach balance is zero:
	if (rhptr->refcount) {
	    HALERR("ring '%s' still attached - refcount=%d",
		   name, rhptr->refcount);
	    return -EBUSY;
	}

	HALDBG("deleting ring '%s'", name);
	if (hrptr->flags & ALLOC_HALMEM) {
	    ; // if there were a HAL memory free function, call it here
	} else {
	    if ((retval = rtapi_shmem_delete(shmid, lib_module_id)) < 0)  {
		HALERR("ring '%s': rtapi_shmem_delete(%d,%d) failed: %d",
		       name, shmid, lib_module_id, retval);
		return retval;
	    }
	}
	// search for the ring (again..)
	int *prev = &(hal_data->ring_list_ptr);
	int next = *prev;
	while (next != 0) {
	    hrptr = SHMPTR(next);
	    if (strcmp(hrptr->name, name) == 0) {
		// this is the right ring
		// unlink from list
		*prev = hrptr->next_ptr;
		// and delete it, linking it on the free list
		free_ring_struct(hrptr);
		return 0;
	    }
	    // no match, try the next one
	    prev = &(hrptr->next_ptr);
	    next = *prev;
	}

	HALERR("BUG: deleting ring '%s'; not found in ring_list?",
	       name);
	return -ENOENT;
    }

}
示例#15
0
文件: hal_funct.c 项目: ArcEye/MK-Qt5
static int hal_export_xfunctfv(const hal_export_xfunct_args_t *xf, const char *fmt, va_list ap)
{
    int *prev, next, cmp, sz;
    hal_funct_t *nf, *fptr;
    char name[HAL_NAME_LEN + 1];

    CHECK_HALDATA();
    CHECK_LOCK(HAL_LOCK_LOAD);

    sz = rtapi_vsnprintf(name, sizeof(name), fmt, ap);
    if(sz == -1 || sz > HAL_NAME_LEN) {
        HALERR("length %d invalid for name starting '%s'", sz, name);
        return -ENOMEM;
    }

    HALDBG("exporting function '%s' type %d", name, xf->type);
    {
	hal_comp_t *comp  __attribute__((cleanup(halpr_autorelease_mutex)));

	/* get mutex before accessing shared data */
	rtapi_mutex_get(&(hal_data->mutex));

	comp = halpr_find_owning_comp(xf->owner_id);
	if (comp == 0) {
	    /* bad comp_id */
	    HALERR("funct '%s': owning component %d not found",
		   name, xf->owner_id);
	    return -EINVAL;
	}

	if (comp->type == TYPE_USER) {
	    /* not a realtime component */
	    HALERR("funct '%s': component %s/%d is not realtime (%d)",
		   name, comp->name, comp->comp_id, comp->type);
	    return -EINVAL;
	}

	bool legacy = (halpr_find_inst_by_id(xf->owner_id) == NULL);

	// instances may export functs post hal_ready
	if (legacy && (comp->state > COMP_INITIALIZING)) {
	    HALERR("funct '%s': called after hal_ready", name);
	    return -EINVAL;
	}
	/* allocate a new function structure */
	nf = alloc_funct_struct();
	if (nf == 0)
	    NOMEM("function '%s'", name);

	/* initialize the structure */
	nf->uses_fp = xf->uses_fp;
	nf->owner_id = xf->owner_id;
	nf->reentrant = xf->reentrant;
	nf->users = 0;
	nf->handle = rtapi_next_handle();
	nf->arg = xf->arg;
	nf->type = xf->type;
	nf->funct.l = xf->funct.l; // a bit of a cheat really
	rtapi_snprintf(nf->name, sizeof(nf->name), "%s", name);
	/* search list for 'name' and insert new structure */
	prev = &(hal_data->funct_list_ptr);
	next = *prev;
	while (1) {
	    if (next == 0) {
		/* reached end of list, insert here */
		nf->next_ptr = next;
		*prev = SHMOFF(nf);
		/* break out of loop and init the new function */
		break;
	    }
	    fptr = SHMPTR(next);
	    cmp = strcmp(fptr->name, nf->name);
	    if (cmp > 0) {
		/* found the right place for it, insert here */
		nf->next_ptr = next;
		*prev = SHMOFF(nf);
		/* break out of loop and init the new function */
		break;
	    }
	    if (cmp == 0) {
		/* name already in list, can't insert */
		free_funct_struct(nf);
		HALERR("duplicate function '%s'", name);
		return -EINVAL;
	    }
	    /* didn't find it yet, look at next one */
	    prev = &(fptr->next_ptr);
	    next = *prev;
	}
	// at this point we have a new function and can
	// yield the mutex by scope exit

    }
    /* init time logging variables */
    nf->runtime = 0;
    nf->maxtime = 0;
    nf->maxtime_increased = 0;

    /* at this point we have a new function and can yield the mutex */
    rtapi_mutex_give(&(hal_data->mutex));

    switch (xf->type) {
    case FS_LEGACY_THREADFUNC:
    case FS_XTHREADFUNC:
	/* create a pin with the function's runtime in it */
	if (hal_pin_s32_newf(HAL_OUT, &(nf->runtime), xf->owner_id, "%s.time",name)) {
	    HALERR("failed to create pin '%s.time'", name);
	    return -EINVAL;
	}
	*(nf->runtime) = 0;

	/* note that failure to successfully create the following params
	   does not cause the "export_funct()" call to fail - they are
	   for debugging and testing use only */
	/* create a parameter with the function's maximum runtime in it */
	nf->maxtime = 0;
	hal_param_s32_newf(HAL_RW,  &(nf->maxtime), xf->owner_id, "%s.tmax", name);

	/* create a parameter with the function's maximum runtime in it */
	nf->maxtime_increased = 0;
	hal_param_bit_newf(HAL_RO, &(nf->maxtime_increased), xf->owner_id,
			    "%s.tmax-inc", name);
	break;
    case FS_USERLAND: // no timing pins/params
	;
    }

    return 0;
}
示例#16
0
文件: hal_funct.c 项目: ArcEye/MK-Qt5
int hal_add_funct_to_thread(const char *funct_name,
			    const char *thread_name, int position)
{
    hal_funct_t *funct;
    hal_list_t *list_root, *list_entry;
    int n;
    hal_funct_entry_t *funct_entry;

    CHECK_HALDATA();
    CHECK_LOCK(HAL_LOCK_CONFIG);
    CHECK_STR(funct_name);
    CHECK_STR(thread_name);

    HALDBG("adding function '%s' to thread '%s'", funct_name, thread_name);
    {
	hal_thread_t *thread __attribute__((cleanup(halpr_autorelease_mutex)));

	/* get mutex before accessing data structures */
	rtapi_mutex_get(&(hal_data->mutex));

	/* make sure position is valid */
	if (position == 0) {
	    /* zero is not allowed */
	    HALERR("bad position: 0");
	    return -EINVAL;
	}

	/* search function list for the function */
	funct = halpr_find_funct_by_name(funct_name);
	if (funct == 0) {
	    HALERR("function '%s' not found", funct_name);
	    return -EINVAL;
	}
	// type-check the functions which go onto threads
	switch (funct->type) {
	case FS_LEGACY_THREADFUNC:
	case FS_XTHREADFUNC:
	    break;
	default:
	    HALERR("cant add type %d function '%s' "
		   "to a thread", funct->type, funct_name);
	    return -EINVAL;
	}
	/* found the function, is it available? */
	if ((funct->users > 0) && (funct->reentrant == 0)) {
	    HALERR("function '%s' may only be added "
		   "to one thread", funct_name);
	    return -EINVAL;
	}
	/* search thread list for thread_name */
	thread = halpr_find_thread_by_name(thread_name);
	if (thread == 0) {
	    /* thread not found */
	    HALERR("thread '%s' not found", thread_name);
	    return -EINVAL;
	}
#if 0
	/* ok, we have thread and function, are they compatible? */
	if ((funct->uses_fp) && (!thread->uses_fp)) {
	    HALERR("function '%s' needs FP", funct_name);
	    return -EINVAL;
	}
#endif
	/* find insertion point */
	list_root = &(thread->funct_list);
	list_entry = list_root;
	n = 0;
	if (position > 0) {
	    /* insertion is relative to start of list */
	    while (++n < position) {
		/* move further into list */
		list_entry = list_next(list_entry);
		if (list_entry == list_root) {
		    /* reached end of list */
		    HALERR("position '%d' is too high", position);
		    return -EINVAL;
		}
	    }
	} else {
	    /* insertion is relative to end of list */
	    while (--n > position) {
		/* move further into list */
		list_entry = list_prev(list_entry);
		if (list_entry == list_root) {
		    /* reached end of list */
		    HALERR("position '%d' is too low", position);
		    return -EINVAL;
		}
	    }
	    /* want to insert before list_entry, so back up one more step */
	    list_entry = list_prev(list_entry);
	}
	/* allocate a funct entry structure */
	funct_entry = alloc_funct_entry_struct();
	if (funct_entry == 0)
	    NOMEM("thread->function link");

	/* init struct contents */
	funct_entry->funct_ptr = SHMOFF(funct);
	funct_entry->arg = funct->arg;
	funct_entry->funct.l = funct->funct.l;
	funct_entry->type = funct->type;
	/* add the entry to the list */
	list_add_after((hal_list_t *) funct_entry, list_entry);
	/* update the function usage count */
	funct->users++;
    }
    return 0;
}