/*
 * Copyright(C) 2010 Крипто-Про, ООО
 *
 * Этот файл содержит информацию, являющуюся
 * собственностью компании Крипто-Про.
 *
 * Любая часть этого файла не может быть скопирована,
 * исправлена, переведена на другие языки,
 * локализована или модифицирована любым способом,
 * откомпилирована, передана по сети с или на
 * любую компьютерную систему без предварительного
 * заключения соглашения с компанией Крипто-Про.
 */

/*!
 * \file $RCSfile$
 * \version $Revision: 147577 $
 * \date $Date:: 2016-10-27 18:56:51 +0300#$
 * \author $Author: sdn $
 *
 * \brief Общая часть обёрток, которые обеспечивают 
 *        защиту от сигналов и имперсонализацию. 
 *        Как для блокирующихся функций, так и для неблокирующихся.
 *		
 *		reader/sup_dlfcn.h
 *		reader/sup_fcntl.h
 *		reader/sup_pwd_grp.h
 *		reader/sup_signal.h
 *		reader/sup_stropts.h
 *		reader/sup_sys_socket.h
 *		reader/sup_sys_stat.h
 *		reader/sup_unistd.h
 *
 */

#ifndef sup_signal_safe_h_
#define sup_signal_safe_h_

#ifdef UNIX
    #include <pthread.h>
#endif
#include <signal.h>

#include "reader/support.h"
#include "reader/sup_errno.h"
#include "reader/sup_poll.h"
#include "reader/sup_sched.h"
#include "reader/sup_signal_nosig.h"

#ifdef __cplusplus
extern "C" {
#endif

/* 
 * EINTR/EAGAIN storm
 *   Если сигналы разрешены, то чем дольше работаем, тем больше их
 *   получаем. Пока поддерживаем парадигму, что сигналы - это дело
 *   приложения, стало быть просто ждёмс.
 *
 *   Аналогично с EAGAIN, чем больше трепыхаемся, тем больше забиваем 
 *   систему, тоже ждёмс.
 *
 *      2^(7*(n-1))
 *      0 - 0		- retry;
 *      1 - 1		- support_yield();
 *      2 - 2..128	- Sleep(1);
 *      3 - 129..16536  - Sleep(4) + времемнно блокируем сигналы;
 *      4 - 16537..     - Sleep(10) + времемнно блокируем сигналы;
 */

static SUP_INLINE unsigned 
support_loglog(unsigned sz)
{
    unsigned ret;

    for (ret = 0; 0 < sz; ret++) {
	sz >>= 1;
    }
    if (0 < ret) {
//	ret = 1 + ret/7;
	ret = 1 + ret/8;
    }
    return ret;
}

static SUP_INLINE unsigned 
support_expexp(unsigned nc)
{
    if (0 == nc) {
	return 0;
    }
    return 1 << (7*(nc-1));
}

static SUP_INLINE void
support_EagnEintr_try(unsigned retry, const TCHAR *sfunc, 
			EagnEintr_set_t *buf, 
			EagnEintr_set_t **ppold, unsigned *pudetect)
{
    unsigned w = support_loglog(retry);
    int serrno;

    if (0 == w) {
	return;
    }
    serrno = SUP_ERRNO;
    if (1 == w) {
	support_yield();
    } else {
	unsigned ms = 1 + (w-1)*3;
	if (2 <= w && NULL == *ppold) {
	    support_EagnEintr_mask(buf, ppold);
	}

	Sleep(ms);

	if (3 <= w && 0 == *pudetect) {
	    *pudetect = retry;
	    DbTrace(DB_ERROR, (EET_FTEXT2(EET_db_ctx,
			"EINTR/EAGAIN storm detect by %s(), ", 
			"retry=%d errno = %s(%d)"),
			sfunc, retry, SUP_STRERROR(serrno), serrno));
	}
    }
    SUP_SETERRNO(serrno);
}

#define SUPPORT_ACTUAL_NOSIG(type, pref, func, args, cargs, noerr, dbtrc) \
	    static SUP_INLINE type support_an_##func args \
	    { \
		type ret = (type)0xffff; \
		int serrno = -1; \
		unsigned retry = 0; \
		EagnEintr_set_t oldmask; \
		EagnEintr_set_t *pold = NULL; \
		unsigned  udetect = 0; \
		int rc = -1; \
            \
                for(; 0 == udetect; \
			support_EagnEintr_try(retry++, SFUNC, &oldmask, \
						&pold, &udetect)) { \
		    if(ERROR_SUCCESS != (rc = \
				    support_thread_actualize_uids())) { \
			DbTrace(DB_ERROR, (EET_FTEXT2(EET_db_ctx, \
				"%s() support_thread_actualize_uids() ", \
				"rc=%d errno = %s(%d)"), \
			     SFUNC, rc, SUP_STRERROR(SUP_ERRNO), SUP_ERRNO)); \
			continue; \
		    } \
		    ret = SUP_SIG_CONS_FN_(pref, func) cargs; \
		    serrno = SUP_ERRNO; \
		    if(ERROR_SUCCESS != (rc = \
				    support_thread_deactualize_uids())) { \
			DbTrace(DB_ERROR, (EET_FTEXT2(EET_db_ctx, \
				"%s() support_thread_deactualize_uids() ", \
				"rc=%d errno = %s(%d)"), \
			     SFUNC, rc, SUP_STRERROR(SUP_ERRNO), SUP_ERRNO)); \
		    } \
		    if(noerr) { \
			goto ret_ret; \
		    } else { \
			switch(serrno) { \
			case SUP_CASE_EAGAIN: \
			case SUP_EINTR: \
			case SUP_EINPROGRESS: \
			    SUP_SETERRNO(serrno); \
			    continue; \
			default: \
			    DbTrace(DB_WARN, dbtrc); \
			    goto ret_ret; \
			} \
		    } \
		} \
		/* support_EagnEintr_try - диагностирует неудачу повторов */ \
		DbTrace(DB_WARN, dbtrc); \
	    ret_ret: \
		support_EagnEintr_final(pold); \
		SUP_SETERRNO(serrno); \
		return ret; \
	    }

// TODO:X support_snb_* -> support_snba_*
#define SUPPORT_SIMPLE_NONBLOCK(evnt, \
			type, pref, func, args, cargs, noerr, dbtrc) \
	    static SUP_INLINE type support_snb_##func args \
	    { \
		type ret = (type)-1; \
		support_pollfd_t fds; \
		int tmout = /*60**/1000; \
		int nfds = -1; \
		int serrno = SUP_ERRNO; \
		unsigned retry = 0; \
		EagnEintr_set_t oldmask; \
		EagnEintr_set_t *pold = NULL; \
		unsigned  udetect = 0; \
		int rc = -1; \
            \
		fds.fd = asocket; \
		fds.events = evnt; \
		fds.revents = 0; \
	    \
                for(; 0 == udetect; \
			support_EagnEintr_try(retry++, SFUNC, &oldmask, \
						&pold, &udetect)) { \
		    if(0 >= (nfds = support_poll(&fds, 1, tmout))) { \
			serrno = SUP_ERRNO; \
			if (0 == nfds) { \
			    continue; \
			} \
			switch(serrno) { \
			case SUP_CASE_EAGAIN: \
			case SUP_EINTR: \
			case SUP_EINPROGRESS: \
			    continue; \
			default: \
			    DbTrace(DB_ERROR, (EET_FTEXT2(EET_db_ctx, \
			    "%s() - support_poll({.fd=%d, .events=%s(0x%x)},",\
			    " %d, %d) = %d fail %s(%d)"), \
				SFUNC, \
				fds.fd, #evnt, (unsigned)fds.events, \
				1, tmout, \
				nfds, SUP_STRERROR(serrno), serrno)); \
			    ret = (type)-1; \
			    goto ret_ret; \
			} \
		    } \
		    if(ERROR_SUCCESS != (rc = \
				    support_thread_actualize_uids())) { \
			DbTrace(DB_ERROR, (EET_FTEXT2(EET_db_ctx, \
				"%s() support_thread_actualize_uids() ", \
				"rc=%d errno = %s(%d)"), \
			     SFUNC, rc, SUP_STRERROR(SUP_ERRNO), SUP_ERRNO)); \
			continue; \
		    } \
		    ret = SUP_SIG_CONS_FN_(pref, func) cargs; \
		    serrno = SUP_ERRNO; \
		    if(ERROR_SUCCESS != (rc = \
				    support_thread_deactualize_uids())) { \
			DbTrace(DB_ERROR, (EET_FTEXT2(EET_db_ctx, \
				"%s() support_thread_deactualize_uids() ", \
				"rc=%d errno = %s(%d)"), \
			     SFUNC, rc, SUP_STRERROR(SUP_ERRNO), SUP_ERRNO)); \
		    } \
		    if(noerr) { \
			goto ret_ret; \
		    } else { \
			switch(serrno) { \
			case SUP_CASE_EAGAIN: \
			case SUP_EINTR: \
			case SUP_EINPROGRESS: \
			    SUP_SETERRNO(serrno); \
			    continue; \
			default: \
			    DbTrace(DB_ERROR, dbtrc); \
			    goto ret_ret; \
			} \
		    } \
		} \
		/* support_EagnEintr_try - диагностирует неудачу повторов */ \
		if(0 >= nfds) { \
		    DbTrace(DB_ERROR, (EET_FTEXT2(EET_db_ctx, \
			    "%s() - support_poll({.fd=%d, .events=%s(0x%x)},",\
			    " %d, %d) nfds=%d errno = %s(%d)"), \
			SFUNC, \
			fds.fd, #evnt, (unsigned)fds.events, \
			1, tmout, \
			nfds, SUP_STRERROR(serrno), serrno)); \
		} \
		DbTrace(DB_ERROR, dbtrc); \
	    ret_ret: \
		support_EagnEintr_final(pold); \
		SUP_SETERRNO(serrno); \
		return ret; \
	    }

#ifdef __cplusplus
}
#endif
#endif /* sup_signal_safe_h_ */
