/*
* Copyright (c) Likewise Software. All rights Reserved.
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the license, or (at
* your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details. You should have received a copy
* of the GNU Lesser General Public License along with this program. If
* not, see .
*
* LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING
* TERMS AS WELL. IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT
* WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE
* TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU
* LESSER GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE. IF YOU
* HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING
* TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT
* license@likewisesoftware.com
*/
/*
* Module Name:
*
* task-select.c
*
* Abstract:
*
* Task manager API (select-based implementation)
*
* Authors: Brian Koropoff (bkoropoff@likewisesoftware.com)
*
*/
#include
#include
#include "task-threadpool-private.h"
static pthread_mutex_t global_manager_lock = PTHREAD_MUTEX_INITIALIZER;
static LWMsgTaskManager* global_manager = NULL;
static unsigned long global_manager_refs;
static struct
{
LWMsgTaskTrigger trigger;
LW_TASK_EVENT_MASK mask;
} mask_table[] =
{
{LWMSG_TASK_TRIGGER_INIT, LW_TASK_EVENT_INIT},
{LWMSG_TASK_TRIGGER_EXPLICIT, LW_TASK_EVENT_EXPLICIT},
{LWMSG_TASK_TRIGGER_CANCEL, LW_TASK_EVENT_CANCEL},
{LWMSG_TASK_TRIGGER_TIME, LW_TASK_EVENT_TIME},
{LWMSG_TASK_TRIGGER_FD_READABLE, LW_TASK_EVENT_FD_READABLE},
{LWMSG_TASK_TRIGGER_FD_WRITABLE, LW_TASK_EVENT_FD_WRITABLE},
{LWMSG_TASK_TRIGGER_FD_EXCEPTION, LW_TASK_EVENT_FD_EXCEPTION},
{0, 0},
};
static
LWMsgTaskTrigger
lwmsg_task_threadpool_event_to_lwmsg_event(
LW_TASK_EVENT_MASK event
)
{
LWMsgTaskTrigger trigger = 0;
unsigned int i = 0;
for (i = 0; mask_table[i].trigger; i++)
{
if (event & mask_table[i].mask)
{
trigger |= mask_table[i].trigger;
}
}
return trigger;
}
static
LW_TASK_EVENT_MASK
lwmsg_task_lwmsg_event_to_threadpool_event(
LWMsgTaskTrigger trigger
)
{
LW_TASK_EVENT_MASK event = 0;
unsigned int i = 0;
for (i = 0; mask_table[i].trigger; i++)
{
if (trigger & mask_table[i].trigger)
{
event |= mask_table[i].mask;
}
}
return event;
}
static
VOID
lwmsg_task_function_proxy(
LW_IN PLW_TASK pTask,
LW_IN LW_PVOID pContext,
LW_IN LW_TASK_EVENT_MASK WakeMask,
LW_IN LW_OUT LW_TASK_EVENT_MASK* pWaitMask,
LW_IN LW_OUT LW_LONG64* pllTime
)
{
LWMsgTask* task = pContext;
LWMsgTaskTrigger wait_trigger = 0;
LWMsgTime new_time = {0, 0};
if (*pllTime < 0)
{
new_time.seconds = -1;
new_time.microseconds = -1;
}
else
{
new_time.seconds = *pllTime / 1000000000ll;
new_time.microseconds = (*pllTime % 1000000000ll) / 1000;
}
task->real_function(
task->task_data,
lwmsg_task_threadpool_event_to_lwmsg_event(WakeMask),
&wait_trigger,
&new_time);
if (lwmsg_time_is_positive(&new_time))
{
*pllTime = new_time.seconds * 1000000000ll + new_time.microseconds * 1000;
}
else
{
*pllTime = 0;
}
*pWaitMask = lwmsg_task_lwmsg_event_to_threadpool_event(wait_trigger);
}
LWMsgStatus
lwmsg_task_new(
LWMsgTaskManager* manager,
LWMsgTaskGroup* group,
LWMsgTaskFunction func,
void* data,
LWMsgTask** task
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
LWMsgTask* my_task = NULL;
BAIL_ON_ERROR(status = LWMSG_ALLOC(&my_task));
my_task->task_data = data;
my_task->real_function = func;
BAIL_ON_ERROR(status = MAP_NTSTATUS(
LwRtlCreateTask(
(PLW_THREAD_POOL) manager,
&my_task->real_task,
(PLW_TASK_GROUP) group,
lwmsg_task_function_proxy,
my_task)));
*task = my_task;
cleanup:
return status;
error:
if (my_task)
{
lwmsg_task_release(my_task);
}
goto cleanup;
}
LWMsgStatus
lwmsg_task_group_new(
LWMsgTaskManager* manager,
LWMsgTaskGroup** group
)
{
return MAP_NTSTATUS(
LwRtlCreateTaskGroup(
(PLW_THREAD_POOL) manager,
(PLW_TASK_GROUP*) (PVOID) group));
}
void
lwmsg_task_release(
LWMsgTask* task
)
{
LwRtlReleaseTask(&task->real_task);
free(task);
}
void
lwmsg_task_group_delete(
LWMsgTaskGroup* group
)
{
LwRtlFreeTaskGroup((PLW_TASK_GROUP*) (PVOID) &group);
}
void
lwmsg_task_set_trigger_fd(
LWMsgTask* task,
int fd
)
{
LwRtlSetTaskFd(task->real_task, fd,
LW_TASK_EVENT_FD_READABLE | LW_TASK_EVENT_FD_WRITABLE);
}
void
lwmsg_task_wake(
LWMsgTask* task
)
{
LwRtlWakeTask(task->real_task);
}
void
lwmsg_task_cancel(
LWMsgTask* task
)
{
LwRtlCancelTask(task->real_task);
}
void
lwmsg_task_wait(
LWMsgTask* task
)
{
LwRtlWaitTask(task->real_task);
}
void
lwmsg_task_group_wake(
LWMsgTaskGroup* group
)
{
LwRtlWakeTaskGroup((PLW_TASK_GROUP) group);
}
void
lwmsg_task_group_cancel(
LWMsgTaskGroup* group
)
{
LwRtlCancelTaskGroup((PLW_TASK_GROUP) group);
}
void
lwmsg_task_group_wait(
LWMsgTaskGroup* group
)
{
LwRtlWaitTaskGroup((PLW_TASK_GROUP) group);
}
LWMsgStatus
lwmsg_task_dispatch_work_item(
LWMsgTaskManager* manager,
LWMsgWorkItemFunction func,
void* data
)
{
return MAP_NTSTATUS(
LwRtlQueueWorkItem(
(PLW_THREAD_POOL) manager,
func,
data,
0));
}
LWMsgStatus
lwmsg_task_acquire_manager(
LWMsgTaskManager** manager
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
pthread_mutex_lock(&global_manager_lock);
if (global_manager)
{
global_manager_refs++;
*manager = global_manager;
}
else
{
BAIL_ON_ERROR(status = MAP_NTSTATUS(
LwRtlCreateThreadPool(
(PLW_THREAD_POOL*) (PVOID) &global_manager)));
global_manager_refs = 1;
*manager = global_manager;
}
error:
pthread_mutex_unlock(&global_manager_lock);
return status;
}
void
lwmsg_task_release_manager(
LWMsgTaskManager* manager
)
{
pthread_mutex_lock(&global_manager_lock);
if (manager == global_manager)
{
if (--global_manager_refs == 0)
{
LwRtlFreeThreadPool((PLW_THREAD_POOL*) (PVOID) &global_manager);
}
}
else
{
LwRtlFreeThreadPool((PLW_THREAD_POOL*) (PVOID) &manager);
}
pthread_mutex_unlock(&global_manager_lock);
}