移植win32程序时,有一个难点就是涉及到内核对象的操作,需要模拟win32的实现。

其中比较奇葩的一个是WaitForSingleObject系列。

Linux中没有类似的timeout实现,模拟这个接口,颇费功夫,做个笔记,以备将来。

头文件

 /*
* WIN32 Events for POSIX
* 模拟win32的Event通知等待
*/ #ifndef __LIBROOT_MY_EVENTS_H_
#define __LIBROOT_MY_EVENTS_H_ #if defined(_WIN32) && !defined(CreateEvent)
#error Must include Windows.h prior to including MyEvent.h!
#endif #ifndef WAIT_TIMEOUT
#include <errno.h>
#define WAIT_TIMEOUT ETIMEDOUT
#endif #include <stdint.h> namespace MY_ENVENT
{
#ifdef _WIN32
typedef HANDLE HEVENT;
#else
//Type declarations
struct my_event_t_;
typedef my_event_t_ * HEVENT;
#endif //WIN32-style functions
HEVENT CreateEvent(bool manualReset = false, bool initialState = false,
const CStdString& strEventName = _T(""));
int DestroyEvent(HEVENT event);
int WaitForEvent(HEVENT event, uint64_t milliseconds = -);
int SetEvent(HEVENT event);
int ResetEvent(HEVENT event); int WaitForMultipleEvents(HEVENT *events, int count, bool waitAll, uint64_t milliseconds);
int WaitForMultipleEvents(HEVENT *events, int count, bool waitAll, uint64_t milliseconds, int &index); #ifdef PULSE
int PulseEvent(HEVENT event);
#endif } #endif

使用mutex和condition来模拟

具体实现

/*
* WIN32 Events for Linux
* Linux实现版本
*/
#include "stdafx.h" #ifndef _WIN32 #include "MyEvent.h"
#include <assert.h>
#include <errno.h>
#include <sys/time.h>
#include <pthread.h> #include <algorithm>
#include <deque> namespace MY_ENVENT
{
struct my_mevent_t_
{
pthread_mutex_t Mutex;
pthread_cond_t CVariable;
pthread_condattr_t CVariable_attr; int RefCount;
union
{
int FiredEvent;
int EventsLeft;
} Status;
bool WaitAll;
bool StillWaiting; void Destroy()
{
pthread_mutex_destroy(&Mutex);
pthread_cond_destroy(&CVariable);
pthread_condattr_destroy(&CVariable_attr);
}
};
typedef my_mevent_t_ *HMEVENT; struct my_mevent_info_t_
{
HMEVENT Waiter;
int WaitIndex;
};
typedef my_mevent_info_t_ *HMEVENT_INFO; struct my_event_t_
{
pthread_cond_t CVariable;
pthread_condattr_t CVariable_attr;
pthread_mutex_t Mutex;
bool AutoReset;
bool State;
std::deque<my_mevent_info_t_> RegisteredWaits;
}; bool RemoveExpiredWaitHelper(my_mevent_info_t_ wait)
{
int result = pthread_mutex_trylock(&wait.Waiter->Mutex); if (result == EBUSY)
{
return false;
} assert(result == ); if (wait.Waiter->StillWaiting == false)
{
--wait.Waiter->RefCount;
assert(wait.Waiter->RefCount >= );
if (wait.Waiter->RefCount == )
{
wait.Waiter->Destroy();
delete wait.Waiter;
}
else
{
result = pthread_mutex_unlock(&wait.Waiter->Mutex);
assert(result == );
} return true;
} result = pthread_mutex_unlock(&wait.Waiter->Mutex);
assert(result == ); return false;
} HEVENT CreateEvent(bool manualReset, bool initialState, const CStdString& strEventName)
{
//unused event name
strEventName.c_str(); HEVENT event = new my_event_t_; pthread_condattr_init(&event->CVariable_attr);
#if _POSIX_MONOTONIC_CLOCK > 0
pthread_condattr_setclock(&event->CVariable_attr, CLOCK_MONOTONIC);
#endif
int result = pthread_cond_init(&event->CVariable, &event->CVariable_attr);
assert(result == ); result = pthread_mutex_init(&event->Mutex, );
assert(result == ); event->State = false;
event->AutoReset = !manualReset; if (initialState)
{
result = SetEvent(event);
assert(result == );
} return event;
} int UnlockedWaitForEvent(HEVENT event, uint64_t milliseconds)
{
int result = ;
if (!event->State)
{
//Zero-timeout event state check optimization
if (milliseconds == )
{
return WAIT_TIMEOUT;
} timespec ts;
if (milliseconds != (uint64_t) -)
{
timeval tv;
gettimeofday(&tv, NULL); uint64_t nanoseconds = ((uint64_t) tv.tv_sec) * * * + milliseconds * * + ((uint64_t) tv.tv_usec) * ; ts.tv_sec = nanoseconds / / / ;
ts.tv_nsec = (nanoseconds - ((uint64_t) ts.tv_sec) * * * );
} do
{
//Regardless of whether it's an auto-reset or manual-reset event:
//wait to obtain the event, then lock anyone else out
if (milliseconds != (uint64_t) -)
{
result = pthread_cond_timedwait(&event->CVariable, &event->Mutex, &ts);
}
else
{
result = pthread_cond_wait(&event->CVariable, &event->Mutex);
}
} while (result == && !event->State); if (result == && event->AutoReset)
{
//We've only accquired the event if the wait succeeded
event->State = false;
}
}
else if (event->AutoReset)
{
//It's an auto-reset event that's currently available;
//we need to stop anyone else from using it
result = ;
event->State = false;
}
//Else we're trying to obtain a manual reset event with a signaled state;
//don't do anything return result;
} int WaitForEvent(HEVENT event, uint64_t milliseconds)
{
int tempResult;
if (milliseconds == )
{
tempResult = pthread_mutex_trylock(&event->Mutex);
if (tempResult == EBUSY)
{
return WAIT_TIMEOUT;
}
}
else
{
tempResult = pthread_mutex_lock(&event->Mutex);
} assert(tempResult == ); int result = UnlockedWaitForEvent(event, milliseconds); tempResult = pthread_mutex_unlock(&event->Mutex);
assert(tempResult == ); return result;
} int WaitForMultipleEvents(HEVENT *events, int count, bool waitAll, uint64_t milliseconds)
{
int unused;
return WaitForMultipleEvents(events, count, waitAll, milliseconds, unused);
} int WaitForMultipleEvents(HEVENT *events, int count, bool waitAll, uint64_t milliseconds, int &waitIndex)
{
HMEVENT wfmo = new my_mevent_t_;
pthread_condattr_init(&wfmo->CVariable_attr);
#if _POSIX_MONOTONIC_CLOCK > 0
pthread_condattr_setclock(&wfmo->CVariable_attr, CLOCK_MONOTONIC);
#endif
int result = ;
int tempResult = pthread_mutex_init(&wfmo->Mutex, );
assert(tempResult == ); tempResult = pthread_cond_init(&wfmo->CVariable, &wfmo->CVariable_attr);
assert(tempResult == ); my_mevent_info_t_ waitInfo;
waitInfo.Waiter = wfmo;
waitInfo.WaitIndex = -; wfmo->WaitAll = waitAll;
wfmo->StillWaiting = true;
wfmo->RefCount = ; if (waitAll)
{
wfmo->Status.EventsLeft = count;
}
else
{
wfmo->Status.FiredEvent = -;
} tempResult = pthread_mutex_lock(&wfmo->Mutex);
assert(tempResult == ); bool done = false;
waitIndex = -; for (int i = ; i < count; ++i)
{
waitInfo.WaitIndex = i; //Must not release lock until RegisteredWait is potentially added
tempResult = pthread_mutex_lock(&events[i]->Mutex);
assert(tempResult == ); //Before adding this wait to the list of registered waits, let's clean up old, expired waits while we have the event lock anyway
events[i]->RegisteredWaits.erase(std::remove_if (events[i]->RegisteredWaits.begin(), events[i]->RegisteredWaits.end(), RemoveExpiredWaitHelper), events[i]->RegisteredWaits.end()); if (UnlockedWaitForEvent(events[i], ) == )
{
tempResult = pthread_mutex_unlock(&events[i]->Mutex);
assert(tempResult == ); if (waitAll)
{
--wfmo->Status.EventsLeft;
assert(wfmo->Status.EventsLeft >= );
}
else
{
wfmo->Status.FiredEvent = i;
waitIndex = i;
done = true;
break;
}
}
else
{
events[i]->RegisteredWaits.push_back(waitInfo);
++wfmo->RefCount; tempResult = pthread_mutex_unlock(&events[i]->Mutex);
assert(tempResult == );
}
} timespec ts;
if (!done)
{
if (milliseconds == )
{
result = WAIT_TIMEOUT;
done = true;
}
else if (milliseconds != (uint64_t) -)
{
timeval tv;
gettimeofday(&tv, NULL); uint64_t nanoseconds = ((uint64_t) tv.tv_sec) * * * + milliseconds * * + ((uint64_t) tv.tv_usec) * ; ts.tv_sec = nanoseconds / / / ;
ts.tv_nsec = (nanoseconds - ((uint64_t) ts.tv_sec) * * * );
}
} while (!done)
{
//One (or more) of the events we're monitoring has been triggered? //If we're waiting for all events, assume we're done and check if there's an event that hasn't fired
//But if we're waiting for just one event, assume we're not done until we find a fired event
done = (waitAll && wfmo->Status.EventsLeft == ) || (!waitAll && wfmo->Status.FiredEvent != -); if (!done)
{
if (milliseconds != (uint64_t) -)
{
result = pthread_cond_timedwait(&wfmo->CVariable, &wfmo->Mutex, &ts);
}
else
{
result = pthread_cond_wait(&wfmo->CVariable, &wfmo->Mutex);
} if (result != )
{
break;
}
}
} waitIndex = wfmo->Status.FiredEvent;
wfmo->StillWaiting = false; --wfmo->RefCount;
assert(wfmo->RefCount >= );
if (wfmo->RefCount == )
{
wfmo->Destroy();
delete wfmo;
}
else
{
tempResult = pthread_mutex_unlock(&wfmo->Mutex);
assert(tempResult == );
} return result;
} int DestroyEvent(HEVENT event)
{
int result = ; result = pthread_mutex_lock(&event->Mutex);
assert(result == );
event->RegisteredWaits.erase(std::remove_if (event->RegisteredWaits.begin(), event->RegisteredWaits.end(), RemoveExpiredWaitHelper), event->RegisteredWaits.end());
result = pthread_mutex_unlock(&event->Mutex);
assert(result == ); result = pthread_cond_destroy(&event->CVariable);
pthread_condattr_destroy(&event->CVariable_attr);
assert(result == ); result = pthread_mutex_destroy(&event->Mutex);
assert(result == ); delete event; return ;
} int SetEvent(HEVENT event)
{
int result = pthread_mutex_lock(&event->Mutex);
assert(result == ); event->State = true; //Depending on the event type, we either trigger everyone or only one
if (event->AutoReset)
{
while (!event->RegisteredWaits.empty())
{
HMEVENT_INFO i = &event->RegisteredWaits.front(); result = pthread_mutex_lock(&i->Waiter->Mutex);
assert(result == ); --i->Waiter->RefCount;
assert(i->Waiter->RefCount >= );
if (!i->Waiter->StillWaiting)
{
if (i->Waiter->RefCount == )
{
i->Waiter->Destroy();
delete i->Waiter;
}
else
{
result = pthread_mutex_unlock(&i->Waiter->Mutex);
assert(result == );
}
event->RegisteredWaits.pop_front();
continue;
} event->State = false; if (i->Waiter->WaitAll)
{
--i->Waiter->Status.EventsLeft;
assert(i->Waiter->Status.EventsLeft >= );
//We technically should do i->Waiter->StillWaiting = Waiter->Status.EventsLeft != 0
//but the only time it'll be equal to zero is if we're the last event, so no one
//else will be checking the StillWaiting flag. We're good to go without it.
}
else
{
i->Waiter->Status.FiredEvent = i->WaitIndex;
i->Waiter->StillWaiting = false;
} result = pthread_mutex_unlock(&i->Waiter->Mutex);
assert(result == ); result = pthread_cond_signal(&i->Waiter->CVariable);
assert(result == ); event->RegisteredWaits.pop_front(); result = pthread_mutex_unlock(&event->Mutex);
assert(result == ); return ;
} //event->State can be false if compiled with WFMO support
if (event->State)
{
result = pthread_mutex_unlock(&event->Mutex);
assert(result == ); result = pthread_cond_signal(&event->CVariable);
assert(result == ); return ;
}
}
else
{
for (size_t i = ; i < event->RegisteredWaits.size(); ++i)
{
HMEVENT_INFO info = &event->RegisteredWaits[i]; result = pthread_mutex_lock(&info->Waiter->Mutex);
assert(result == ); --info->Waiter->RefCount;
assert(info->Waiter->RefCount >= ); if (!info->Waiter->StillWaiting)
{
if (info->Waiter->RefCount == )
{
info->Waiter->Destroy();
delete info->Waiter;
}
else
{
result = pthread_mutex_unlock(&info->Waiter->Mutex);
assert(result == );
}
continue;
} if (info->Waiter->WaitAll)
{
--info->Waiter->Status.EventsLeft;
assert(info->Waiter->Status.EventsLeft >= );
//We technically should do i->Waiter->StillWaiting = Waiter->Status.EventsLeft != 0
//but the only time it'll be equal to zero is if we're the last event, so no one
//else will be checking the StillWaiting flag. We're good to go without it.
}
else
{
info->Waiter->Status.FiredEvent = info->WaitIndex;
info->Waiter->StillWaiting = false;
} result = pthread_mutex_unlock(&info->Waiter->Mutex);
assert(result == ); result = pthread_cond_signal(&info->Waiter->CVariable);
assert(result == );
}
event->RegisteredWaits.clear(); result = pthread_mutex_unlock(&event->Mutex);
assert(result == ); result = pthread_cond_broadcast(&event->CVariable);
assert(result == );
} return ;
} int ResetEvent(HEVENT event)
{
int result = pthread_mutex_lock(&event->Mutex);
assert(result == ); event->State = false; result = pthread_mutex_unlock(&event->Mutex);
assert(result == ); return ;
} #ifdef PULSE
int PulseEvent(HEVENT event)
{
//This may look like it's a horribly inefficient kludge with the sole intention of reducing code duplication,
//but in reality this is what any PulseEvent() implementation must look like. The only overhead (function
//calls aside, which your compiler will likely optimize away, anyway), is if only WFMO auto-reset waits are active
//there will be overhead to unnecessarily obtain the event mutex for ResetEvent() after. In all other cases (being
//no pending waits, WFMO manual-reset waits, or any WFSO waits), the event mutex must first be released for the
//waiting thread to resume action prior to locking the mutex again in order to set the event state to unsignaled,
//or else the waiting threads will loop back into a wait (due to checks for spurious CVariable wakeups). int result = SetEvent(event);
assert(result == );
result = ResetEvent(event);
assert(result == ); return ;
}
#endif
} #endif //_WIN32

C++ Code

win32的实现直接套用win api即可,这里就不贴了。

跨平台的WatiForSingleObject实现的更多相关文章

  1. .NET Core 首例 Office 开源跨平台组件(NPOI Core)

    前言 最近项目中,需要使用到 Excel 导出,找了一圈发现没有适用于 .NET Core的,不依赖Office和操作系统限制的 Office 组件,于是萌生了把 NPOI 适配并移植到 .NET C ...

  2. dotNET跨平台相关文档整理

    一直在从事C#开发的相关技术工作,从C# 1.0一路用到现在的C# 6.0, 通常情况下被局限于Windows平台,Mono项目把我们C#程序带到了Windows之外的平台,在工作之余花了很多时间在M ...

  3. Mono为何能跨平台?聊聊CIL(MSIL)

    前言: 其实小匹夫在U3D的开发中一直对U3D的跨平台能力很好奇.到底是什么原理使得U3D可以跨平台呢?后来发现了Mono的作用,并进一步了解到了CIL的存在.所以,作为一个对Unity3D跨平台能力 ...

  4. TinyWeb v1.0 正式完成第一个Release版本(功能基于 libuv 跨平台库)

    使用方法很简单,很容易融入现有项目,使现有项目拥有Web网站功能和WebSocket,以及Socket直连! 并且包含了一个跨平台(windows/linux)工具集合; 嗯,也挺棒的^,^ 在项目中 ...

  5. 开源一个跨平台运行的服务插件 - TaskCore.MainForm

    本次将要很大家分享的是一个跨平台运行的服务插件 - TaskCore.MainForm,此框架是使用.netcore来写的,现在netcore已经支持很多系统平台运行了,所以将以前的Task.Main ...

  6. Xamarin+Prism开发详解一:PCL跨平台类库与Profile的关系

    在[Xamarin+Prism小试牛刀:定制跨平台Outlook邮箱应用]中提到过以下错误,不知道大伙还记得不: 无法安装程序包"Microsoft.Identity.Client 1.0. ...

  7. Xamarin+Prism小试牛刀:定制跨平台Outlook邮箱应用

    通过本文你将学会如下内容: 1,如何使用Xamarin开发跨平台(Windows,Android,iOS)应用. 2,如何使用微软的登录界面登入Microsoft账号. 3,如何使用Outlook邮箱 ...

  8. Xamarin+Prism小试牛刀:定制跨平台Outlook邮箱应用(后续)

    在[Xamarin+Prism小试牛刀:定制跨平台Outlook邮箱应用]里面提到了Microsoft 身份认证,其实这也是一大块需要注意的地方,特作为后续补充这些知识点.上章是使用了Microsof ...

  9. H5程序员如何利用cordova开发跨平台应用

    什么是Cordova? Cordova以前也叫PhoneGap,它提供了一组设备相关的API,通过这组API,移动应用能够以JavaScript访问原生的设备功能,如摄像头.麦克风等.Cordova还 ...

随机推荐

  1. Sublime_text3怎么发现PHP语法错误?

    昨晚因为php的某个变量代码写错了,sublime又没有提示语法错误.弄了许久,一段段的调试,最后才知道是取到的变量是空的 sublime可以提示php语法错误 在sublime写完了php代码后,如 ...

  2. 日志分析(php+nosql+rsync+crontable)

    是不是经常要分析用户的行为?是不是经常遇到多台server上传的日志一起分析?是不是对数据统计的间隔时间要求非常短?还有木有由于日志文件过大,而须要分块处理? 1.说明一点在日志写入的时候必须依照一种 ...

  3. Java中的守护线程 & 非守护线程(简介)

    Java中的守护线程 & 非守护线程 守护线程 (Daemon Thread) 非守护线程,又称用户线程(User Thread) 用个比较通俗的比如,任何一个守护线程都是整个JVM中所有非守 ...

  4. mysql日期时间函数2

    win7可以设定每周从哪一天开始,win2003等不能方便的修改.有的是周日开始,有的是周一开始.而工作中有的时候每周是从周六开始算的,有些数据需要按周统计,那么那种方式比较好呢?   通过下面的研究 ...

  5. App升级时数据库的迁移更新

    前一段时间在进行App升级的时候,由于一开始版本初期没有考虑完善,导致走了很多弯路,后来经过自己的一些思考,总结出了一些在app升级的时候,数据库内文件同步保持更新的经验,希望能给大家带来帮助. 总体 ...

  6. JavaScript 开发规范要求详解

    作为一名开发人员(We前端JavaScript开发),不规范的开发不仅使日后代码维护变的困难,同时也不利于团队的合作,通常还会带来代码安全以及执行效率上的问题.本人在开发工作中就曾与不按规范来开发的同 ...

  7. X86(32位)与X64(64位)有什么区别,如何选择对应的操作系统和应用程序?

    X86就是我们一般用的32位的系统,指针长度为32位(386起):X64就是64位的系统,指针长度为64位. 选择硬件对应的软件,建议通过以下三条考虑:1.64位操作系统相对32位操作系统理论上性能会 ...

  8. XML--小结①

  9. [Cookie] C#CookieHelper--C#操作Cookie的帮助类 (转载)

    点击下载 CookieHelper.rar 下面是代码大家看一下 /// <summary> /// 类说明:CookieHelper /// 联系方式:361983679 /// 更新网 ...

  10. OC基础-day02

    #pragma mark - Day02_01_对象的创建与使用 1)如何通过类创建一个对象 1. 类是抽象的,无法直接使用 2. 对象是类的一个具体实现,可以直接使用 3. 语法 类名 *对象名 = ...