403Webshell
Server IP : 189.126.111.107  /  Your IP : 216.73.217.69
Web Server : Apache
System : Linux www.gadotticar.com.br 5.4.0-135-generic #152-Ubuntu SMP Wed Nov 23 20:19:22 UTC 2022 x86_64
User : gadotticar ( 1000)
PHP Version : 8.2.27
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : ON  |  Pkexec : ON
Directory :  /usr/src/libks/src/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /usr/src/libks/src/ks_thread.c
/*
 * Copyright (c) 2018-2023 SignalWire, Inc
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include "libks/ks.h"
#include "libks/ks_atomic.h"
#include "libks/internal/ks_thread.h"

/* Keep some basic counters for some basic debugging info when needed */
static uint32_t g_active_detached_thread_count = 0, g_active_attached_thread_count = 0;

#ifdef WIN32
	/* Setup for thread name setting, pulled from MSDN example */
	const DWORD MS_VC_EXCEPTION = 0x406D1388;
	#pragma pack(push,8)
	typedef struct tagTHREADNAME_INFO
	{
		DWORD dwType; // Must be 0x1000.
		LPCSTR szName; // Pointer to name (in user addr space).
		DWORD dwThreadID; // Thread ID (-1=caller thread).
		DWORD dwFlags; // Reserved for future use, must be zero.
	 } THREADNAME_INFO;
	#pragma pack(pop)
	void SetThreadName(DWORD dwThreadID, const char* threadName) {
		THREADNAME_INFO info;
		info.dwType = 0x1000;
		info.szName = threadName;
		info.dwThreadID = dwThreadID;
		info.dwFlags = 0;
	#pragma warning(push)
	#pragma warning(disable: 6320 6322)
		__try{
			RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
		}
		__except (EXCEPTION_EXECUTE_HANDLER){
		}
	#pragma warning(pop)
	}
#endif

KS_DECLARE(ks_thread_os_handle_t) ks_thread_os_handle(ks_thread_t *thread)
{
	return thread->handle;
}

KS_DECLARE(ks_thread_os_handle_t) ks_thread_self(void)
{
#ifdef WIN32
	return GetCurrentThread();
#else
	return pthread_self();
#endif
}

KS_DECLARE(ks_pid_t) ks_thread_self_id(void)
{
#ifdef KS_PLAT_WIN
	return GetCurrentThreadId();
#elif defined(KS_PLAT_LIN)
	return syscall(SYS_gettid);
#elif defined(KS_PLAT_MAC)
	uint64_t tid;
	int r = pthread_threadid_np(NULL, &tid);

	if (!r) {
		return tid;
	} else {
		ks_log(KS_LOG_CRIT, "pthread_threadid_np error, return %d", r);
		ks_log(KS_LOG_CRIT, "BACKTRACE:");
		ks_debug_dump_backtrace();
		abort();
	}
#else
	return pthread_self();
#endif
}

/**
 * Destroys a thread context, may be called by the caller or the thread itself
 * (if the thread is marked as detached).
 */
static ks_status_t __ks_thread_destroy_ex(ks_thread_t **threadp, ks_bool_t internal_call)
{
	ks_thread_t *thread = NULL;
	ks_bool_t detached;
	ks_status_t status = KS_STATUS_FAIL;

	if (!threadp || !*threadp)
		return status;

	thread = *threadp;

	detached = (thread->flags & KS_THREAD_FLAG_DETACHED) ? KS_TRUE : KS_FALSE;

	if (!internal_call && detached) {
		ks_log(KS_LOG_ERROR, "Detached thread cannot be explicitly destroyed. Thread: %p, tid: %"KS_PID_FMT, (void *)thread, thread->id);
		return status;
	}

	ks_mutex_lock(thread->mutex);
	if (thread->in_use) {
		ks_mutex_unlock(thread->mutex);
		ks_log(KS_LOG_ERROR, "Thread still in use. Shut worker first. Thread: %p, tid: %"KS_PID_FMT, (void *)thread, thread->id);
		return status;
	}
	ks_mutex_unlock(thread->mutex);

	ks_log(KS_LOG_DEBUG, "Thread destroy complete, deleting os primitives for thread address %p, tid: %"KS_PID_FMT, (void *)thread, thread->id);

#ifdef WIN32
	CloseHandle(thread->handle);
	thread->handle = NULL;
#else
	pthread_attr_destroy(&thread->attribute);
#endif

	ks_mutex_destroy(&thread->mutex);

	ks_log(KS_LOG_DEBUG, "Current active and attached count: %u, current active and detatched count: %u\n",
		g_active_attached_thread_count, g_active_detached_thread_count);

	if (detached) {
		ks_atomic_decrement_uint32(&g_active_detached_thread_count);
	} else {
		ks_atomic_decrement_uint32(&g_active_attached_thread_count);
	}

	ks_pool_t *pool_to_destroy = (*threadp)->pool_to_destroy;
	if (pool_to_destroy) {
		/* This thread owns all the memory- free its pool */
		ks_pool_close(&pool_to_destroy);
		*threadp = NULL;
	} else {
		/* Free the memory from the pool given to the thread */
		ks_pool_free(threadp);
	}

	status = KS_STATUS_SUCCESS;

	return status;
}

KS_DECLARE(ks_status_t) ks_thread_destroy(ks_thread_t **threadp) {

	return __ks_thread_destroy_ex(threadp, KS_FALSE);

}

static void *KS_THREAD_CALLING_CONVENTION thread_launch(void *args)
{
	ks_thread_t *thread = (ks_thread_t *) args;
	void *ret = NULL;

	ks_log(KS_LOG_DEBUG, "Thread has launched with address: %p, tid: %"KS_PID_FMT"\n", (void *)thread, thread->id);

	thread->id = ks_thread_self_id();

#if KS_PLAT_WIN
	if (thread->tag)
		SetThreadName(thread->id, thread->tag);
#elif KS_PLAT_MAC
	if (thread->tag && &pthread_setname_np)
		pthread_setname_np(thread->tag);
#else
	if (thread->tag && &pthread_setname_np)
		pthread_setname_np(pthread_self(), thread->tag);
#endif

	ks_log(KS_LOG_DEBUG, "START call user thread callback with address: %p, tid: %"KS_PID_FMT"\n", (void *)thread, thread->id);
	ret = thread->function(thread, thread->private_data);
	ks_log(KS_LOG_DEBUG, "STOP call user thread callback with address: %p, tid: %"KS_PID_FMT"\n", (void *)thread, thread->id);

	if (thread->flags & KS_THREAD_FLAG_DETACHED) {
		thread->in_use = KS_FALSE;
		__ks_thread_destroy_ex(&thread, KS_TRUE);
	} else {
		ks_thread_set_return_data(thread, ret);
		ks_mutex_lock(thread->mutex);
		thread->in_use = KS_FALSE;
		ks_mutex_unlock(thread->mutex);
	}

	return ret;
}

KS_DECLARE(int) ks_thread_set_priority(int nice_val)
{
#ifdef WIN32
    SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
#else
#ifdef USE_SCHED_SETSCHEDULER
    /*
     * Try to use a round-robin scheduler
     * with a fallback if that does not work
     */
    struct sched_param sched = { 0 };
    sched.sched_priority = KS_PRI_LOW;
    if (sched_setscheduler(0, SCHED_FIFO, &sched)) {
        sched.sched_priority = 0;
        if (sched_setscheduler(0, SCHED_OTHER, &sched)) {
            return -1;
        }
    }
#endif

	if (nice_val) {
#ifdef HAVE_SETPRIORITY
		/*
		 * setpriority() works on FreeBSD (6.2), nice() doesn't
		 */
		if (setpriority(PRIO_PROCESS, getpid(), nice_val) < 0) {
			ks_log(KS_LOG_CRIT, "Could not set nice level\n");
			return -1;
		}
#else
		if (nice(nice_val) != nice_val) {
			ks_log(KS_LOG_CRIT, "Could not set nice level\n");
			return -1;
		}
#endif
	}
#endif

    return 0;
}

KS_DECLARE(uint8_t) ks_thread_priority(ks_thread_t *thread) {

	ks_assert(thread);

	uint8_t priority = 0;
#ifdef WIN32
	//int pri = GetThreadPriority(thread->handle);

	//if (pri >= THREAD_PRIORITY_TIME_CRITICAL) {
	//	priority = 99;
	//} else if (pri >= THREAD_PRIORITY_ABOVE_NORMAL) {
	//	priority = 50;
	//} else {
	//	priority = 10;
	//}
	priority = thread->priority;
#else
	int policy;
	struct sched_param param = { 0 };

	pthread_getschedparam(thread->handle, &policy, &param);
	priority = param.sched_priority;
#endif
	return priority;
}

static ks_status_t __join_os_thread(ks_thread_t *thread) {
	if (ks_thread_self_id() != thread->id) {
		ks_log(KS_LOG_DEBUG, "Joining on thread address: %p, tid: %"KS_PID_FMT"\n", (void *)thread, thread->id);
#ifdef WIN32
		ks_assertd(WaitForSingleObject(thread->handle, INFINITE) == WAIT_OBJECT_0);
#else
		int err = 0;
		if ((err = pthread_join(thread->handle, NULL)) != 0 && err != ESRCH) {
			ks_log(KS_LOG_DEBUG, "Failed to join on thread address: %p, tid: %"KS_PID_FMT", error = %s\n", (void *)thread, thread->id, strerror(err));
			return KS_STATUS_FAIL;
		}
#endif
		ks_log(KS_LOG_DEBUG, "Completed join on thread address: %p, tid: %"KS_PID_FMT"\n", (void *)thread, thread->id);
	} else {
		ks_log(KS_LOG_DEBUG, "Not joining on self address: %p, tid: %"KS_PID_FMT"\n", (void *)thread, thread->id);
	}
	return KS_STATUS_SUCCESS;
}

KS_DECLARE(ks_status_t) ks_thread_join(ks_thread_t *thread) {
	ks_log(KS_LOG_DEBUG, "Join requested by thread: %"KS_PID_FMT" for thread address: %p, tid: %"KS_PID_FMT"\n", ks_thread_self_id(), (void *)&thread, thread->id);

	return __join_os_thread(thread);
}

/**
 * Flag thread to stop
 */
KS_DECLARE(ks_status_t) ks_thread_request_stop(ks_thread_t *thread)
{
	thread->stop_requested = KS_TRUE;
	return KS_STATUS_SUCCESS;
}

/**
 * Returns true if the thread was requested to exit by the caller
 */
KS_DECLARE(ks_bool_t) ks_thread_stop_requested(ks_thread_t *thread)
{
	return thread->stop_requested;
}

#if KS_PLAT_WIN
/* Windows thread startup */
static ks_status_t __init_os_thread(ks_thread_t *thread)
{
	thread->handle = (void *) _beginthreadex(
		NULL,
		(unsigned) thread->stack_size,
		(unsigned int (__stdcall *) (void *)) thread_launch,
		thread,
		0,
		NULL);

	if (!thread->handle) {
		ks_log(KS_LOG_CRIT, "System failed to allocate thread, lasterror: %d\n", GetLastError());
		return KS_STATUS_FAIL;
	}

	if (thread->priority >= 99) {
		SetThreadPriority(thread->handle, THREAD_PRIORITY_TIME_CRITICAL);
	} else if (thread->priority >= 50) {
		SetThreadPriority(thread->handle, THREAD_PRIORITY_ABOVE_NORMAL);
	} else if (thread->priority >= 10) {
		SetThreadPriority(thread->handle, THREAD_PRIORITY_NORMAL);
	} else if (thread->priority >= 1) {
		SetThreadPriority(thread->handle, THREAD_PRIORITY_LOWEST);
	}

	return KS_STATUS_SUCCESS;
}

#else

static int __init_os_thread_set_priority(ks_thread_t *thread)
{
	int schedpolicy = SCHED_FIFO;
	int inheritsched = PTHREAD_EXPLICIT_SCHED;
	struct sched_param param = { 0 };
	int ret = -1;

	if ((ret = pthread_attr_getschedparam(&thread->attribute, &param)) != 0)
		goto done;

	param.sched_priority = thread->priority;

	if ((ret = pthread_attr_setinheritsched(&thread->attribute, inheritsched)) != 0)
		goto done;

	if ((ret = pthread_attr_setschedpolicy(&thread->attribute, schedpolicy)) != 0)
		goto done;

	if ((ret = pthread_attr_setschedparam(&thread->attribute, &param)) != 0)
		goto done;

	done:

	return ret;
}

/* Gnu thread startup */
static ks_status_t __init_os_thread(ks_thread_t *thread)
{
	ks_status_t status = KS_STATUS_FAIL;
	int err;

	if (pthread_attr_init(&thread->attribute) != 0)
		return status;

	if ((thread->flags & KS_THREAD_FLAG_DETACHED) && pthread_attr_setdetachstate(&thread->attribute, PTHREAD_CREATE_DETACHED) != 0)
		goto done;

	if (thread->stack_size && pthread_attr_setstacksize(&thread->attribute, thread->stack_size) != 0)
		goto done;

#ifdef HAVE_PTHREAD_ATTR_SETSCHEDPARAM
	if (thread->priority) {
		if((err = __init_os_thread_set_priority(thread)) != 0) {
			ks_log(KS_LOG_WARNING, "Setting of schedule attributes failed. Giving a try to run thread with default settings. Error details: %s\n", strerror(err));

			if (pthread_attr_destroy(&thread->attribute) != 0)
				return status;

			if (pthread_attr_init(&thread->attribute) != 0)
				return status;
		}
	}
#endif

	ks_mutex_lock(thread->mutex);
	thread->in_use = KS_TRUE;

	if ((err = pthread_create(&thread->handle, &thread->attribute, thread_launch, thread)) != 0) {
		thread->in_use = KS_FALSE;

		if (err != EPERM) {
			ks_log(KS_LOG_ERROR, "Thread cannot be created. Error details: %s\n", strerror(err));
			ks_mutex_unlock(thread->mutex);
			goto done;
		}

		ks_log(KS_LOG_WARNING, "Not sufficient permissions to set the scheduling policy and parameters specified in attribute. Giving a try to run thread with default settings\n");

		if (pthread_attr_destroy(&thread->attribute) != 0) {
			ks_mutex_unlock(thread->mutex);
			return status;
		}

		if (pthread_attr_init(&thread->attribute) != 0) {
			ks_mutex_unlock(thread->mutex);
			return status;
		}

		thread->in_use = KS_TRUE;

		if (pthread_create(&thread->handle, &thread->attribute, thread_launch, thread) != 0) {
			thread->in_use = KS_FALSE;
			ks_mutex_unlock(thread->mutex);
			goto done;
		}
	}

	ks_mutex_unlock(thread->mutex);
	status = KS_STATUS_SUCCESS;

done:
	/* Cleanup if we failed past alloc of the attributes */
	if (status != KS_STATUS_SUCCESS)
		pthread_attr_destroy(&thread->attribute);

	return status;
}
#endif

KS_DECLARE(ks_status_t) __ks_thread_create_ex(
	ks_thread_t **rthread,
	ks_thread_function_t func,
	void *data,
	uint32_t flags,
	size_t stack_size,
	ks_thread_priority_t priority,
	ks_pool_t *pool,
	const char *file,
	int line,
	const char *tag)
{
	ks_thread_t *thread = NULL;
	ks_status_t status = KS_STATUS_FAIL;

	if (!rthread) return status;

	*rthread = NULL;

	if (!func) return status;

	if (flags & KS_THREAD_FLAG_DETACHED) {
		/* Detached thread owns its own pool */
		if (pool) {
			ks_log(KS_LOG_WARNING, "Ignoring pool passed to ks_thread_create. Detached threads create their own pool.\n");
			pool = NULL;
		}
		ks_pool_open(&pool);
	}
	thread = (ks_thread_t *) __ks_pool_alloc(pool, sizeof(ks_thread_t), file, line, tag);
	if (flags & KS_THREAD_FLAG_DETACHED) {
		thread->pool_to_destroy = pool;
	}

	ks_assertd(thread);

	/* Assign the callers ptr *right* away so the thread doesn't start before its assigned */
	*rthread = thread;

	/* Increment out stats */
	if (flags & KS_THREAD_FLAG_DETACHED) {
		ks_atomic_increment_uint32(&g_active_detached_thread_count);
	} else  {
		ks_atomic_increment_uint32(&g_active_attached_thread_count);
	}

	ks_log(KS_LOG_DEBUG, "Allocating new thread, current active and attached count: %u, current active and detatched count: %u\n",
		g_active_attached_thread_count, g_active_detached_thread_count);

	ks_mutex_create(&thread->mutex, KS_MUTEX_FLAG_DEFAULT, pool);
	thread->in_use = KS_FALSE;
	thread->private_data = data;
	thread->function = func;
	thread->stack_size = stack_size;
	thread->flags = flags;
	thread->priority = priority;
	thread->tag = tag;	/* We require a constant literal string here */

	/* Now allocate the os thread */
	if (__init_os_thread(thread) != KS_STATUS_SUCCESS)  {
		ks_log(KS_LOG_CRIT, "Failed to allocate os thread context for thread address: %p\n", (void *)thread);
		goto done;
	}

	/* Success! */
	status = KS_STATUS_SUCCESS;

  done:
	if (status != KS_STATUS_SUCCESS) {
		ks_log(KS_LOG_CRIT, "Thread allocation failed for thread address: %p\n", (void *)thread);
		__ks_thread_destroy_ex(&thread, KS_TRUE);
		*rthread = NULL;
	}

	return status;
}

KS_DECLARE(void) ks_thread_set_return_data(ks_thread_t *thread, void *return_data)
{
	thread->return_data = return_data;
}

/**
 *  Return data implicitly joins on the thread and returns the
 *  value that the thread itself returned from its callback.
 */
KS_DECLARE(void *) ks_thread_get_return_data(ks_thread_t *thread)
{
	void *thread_data;
	ks_status_t status;

	/* Join if needed (will assert thread is not detached) */
	if (status = ks_thread_join(thread)) {
		ks_log(KS_LOG_ERROR, "Return data blocked, thread join failed: %d\n", status);
		return NULL;
	}

	thread_data = thread->return_data;

	return thread_data;
}

KS_DECLARE(void) ks_thread_stats(uint32_t *active_attached, uint32_t *active_detached)
{
	/* Lean on the fact that integer assignments are atomic */
	if (active_detached) {
		*active_detached = g_active_detached_thread_count;
	}
	if (active_attached) {
		*active_attached = g_active_attached_thread_count;
	}
}

/* For Emacs:
 * Local Variables:
 * mode:c
 * indent-tabs-mode:t
 * tab-width:4
 * c-basic-offset:4
 * End:
 * For VIM:
 * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
 */

Youez - 2016 - github.com/yon3zu
LinuXploit