How can I check receive message in MPI - parallel-processing

I am trying to write MPI Send/Receive code but I have a problem. I want to check receive message. If I have a message from any source, read this. Otherwise, continue code.
MPI_Comm_size(MPI_COMM_WORLD,&p);
MPI_Comm_rank(MPI_COMM_WORLD,&id);
MPI_Status status;
int source,ask,request,answer,tag=10;
for(i=first; i<last; i++){
if(there is a message for my id){
MPI_Recv(&request,1,MPI_INT,MPI_ANY_SOURCE,tag,MPI_COMM_WORLD,&status);
answer = request*request;
source = status.MPI_SOURCE;
MPI_Send(&answer,1,MPI_INT,source,tag+1,MPI_COMM_WORLD);
}
square=i*i;
if(last < square){
MPI_Send(&square,1,MPI_INT,id-1,tag,MPI_COMM_WORLD);
MPI_Recv(&square,1,MPI_INT,id-1,tag+1,MPI_COMM_WORLD,&status);
}
local[i]=square;
}
If I can't check receive message with if statement, Can I check with timeout or wait expression?
*I know this code trivial but I hope it can help us.
Thanks

It's a little hard to tell exactly what you want, but it sounds like what you might be looking for is either a non-blocking receive paired with a test or an MPI_IMPROBE paired with an MPI_MRECV.
So for the non-blocking receive version, your code would end up looking more like:
int flag = 0;
for (i=first; i<last; i++) {
MPI_Irecv(&data, 1, MPI_INT, MPI_ANY_SOURCE, tag, MPI_COMM_WORLD, &mpi_request);
MPI_Test(&mpi_request, &flag, &status);
while (!flag) {
// Do other stuff
MPI_Test(&mpi_request, &flag, &status);
}
// Do stuff with message
}
The version with probe would be slightly different, but in the end, pretty similar:
MPI_Message message;
int flag;
for (i=first; i<last; i++) {
MPI_Improbe(MPI_ANY_SOURCE, tag, MPI_COMM_WORLD, &flag, &message, &status);
while (!flag) {
// Do other stuff
MPI_Test(&mpi_request, &flag, &status);
}
MPI_Mrecv(&data, 1, MPI_INT, &message, &status);
// Do stuff with message
}

Related

How to use IcmpSendEcho2() with APC callbacks in main thread?

First, the documentation for IcmpSendEcho2() contradicts itself:
It says:
The IcmpSendEcho2 function is called synchronously if the ApcRoutine or Event parameters are NULL
Then it says:
The IcmpSendEcho2 function is called asynchronously when either the ApcRoutine or Event parameters are specified
I presume the first one should be "if the ApcRoutine AND Event paramters are NULL"?
Also, it says under the return value:
When called asynchronously, the IcmpSendEcho2 function returns ERROR_IO_PENDING to indicate the operation is in progress
But I don't see that, I see it return 0 and GetLastError() returns ERROR_IO_PENDING. So, can both cases exist, or is the documentation completely wrong?
Now on to the next issue. I wanted to use IcmpSendEcho2() asynchronously using the ACP callback without events. This way, I didn't have to worry about resources should the number of hosts to process be extremely large. However, it doesn't work because no callback occurs. I found this in the documentation under the AcpRoutine parameter:
The routine that is called when the calling thread is in an alertable thread and an ICMPv4 reply arrives.
So I believe my problem is the main thread is not in an alterable state. Since I don't have an event to wait on, and I don't want to wait beyond the time it takes to complete everything, how do I put the main thread in an alterable state without having to guess using something like SleepEx()? Also, if I did use something like SleepEx(10,TRUE), would all the callbacks occur, or do you have to sit in a loop?
My callback context structure includes a shared global OutstandingCount type variable so I'd know when all requests were completed.
Also the ReplyBuffer is in the context structure. Another little nugget hidden in the documentation regarding the ReplyBuffer when using it asynchronously is:
The application must parse the data pointed to by ReplyBuffer parameter using the IcmpParseReplies function
So, the main question here: How are you supposed to properly use the IcmpSendEcho2() function with a AcpRoutine and no Event in a main thread?
-- Update --
Not sure if I should ask an entirely new question but now a problem where it doesn't call the ApcRoutine for every IcmpSendEcho2Ex() sent. The following code works for my normal network adapters (which are 255.255.255.0) but hangs for a 255.255.0.0 network because the outstandingcount never gets to zero.
The adapter it hangs on is:
VirtualBox Host-Only Ethernet Adapter
DHCP Enable: Yes
Autoconfiguration Enabled: Yes
Autoconfiguration IPv4Address: 169.254.21.120
Subnet Mask: 255.255.0.0
Also wonder how long it would take on networks like 10. with a subnet of 255.0.0.0.
Here's the code that starts with the IPV4Scan() built as x64 on Win10 x64:
#define PIO_APC_ROUTINE_DEFINED
#include <winternl.h>
#include <iphlpapi.h>
#include <IcmpAPI.h>
//--------------
// types
//--------------
typedef DWORD (WINAPI *LPFN_IcmpSendEcho2)(HANDLE, HANDLE , PIO_APC_ROUTINE, PVOID, IPAddr, LPVOID, WORD, PIP_OPTION_INFORMATION, LPVOID, DWORD, DWORD);
typedef DWORD (WINAPI *LPFN_IcmpSendEcho2Ex)(HANDLE, HANDLE , PIO_APC_ROUTINE, PVOID, IPAddr, IPAddr, LPVOID, WORD, PIP_OPTION_INFORMATION, LPVOID, DWORD, DWORD);
typedef HANDLE (WINAPI *LPFN_IcmpCreateFile)();
typedef BOOL (WINAPI *LPFN_IcmpCloseHandle)(HANDLE);
typedef DWORD (WINAPI *LPFN_IcmpParseReplies)(LPVOID, DWORD);
BYTE PingSignature[]={ 'X', 'Y', 'Z', '1', '2', '3', '4', '5', '6', '7', '8' };
typedef struct _sPingContext
{
ULONG *OutstandingCount; // shared number of pings outstanding
CMutex *Mutex; // mutex for ipsfound
CNumericBuffer<uint32_t> *IPsFound; // list of ips found (MSBF format)
LPFN_IcmpParseReplies fnIcmpParseReplies; // function pointer
BYTE ReplyBuffer[sizeof(ICMP_ECHO_REPLY) + sizeof(PingSignature) + sizeof(IO_STATUS_BLOCK) + 8]; // reply buffer (see API docs)
_sPingContext(ULONG *outstandingcount, CMutex *mutex, CNumericBuffer<uint32_t> *ipsfound, LPFN_IcmpParseReplies fnicmpparsereplies)
{
OutstandingCount=outstandingcount;
Mutex=mutex;
IPsFound=ipsfound;
fnIcmpParseReplies=fnicmpparsereplies;
memset(ReplyBuffer, 0, sizeof(ReplyBuffer));
};
} sPingContext, *psPingContext;
//-------------------------------------------------------------------------
// Purpose: Callback for async ping
//
// Input: ioresult - [i] io result of async operation
// pingccontext - [i] context passed on ping
// replysize - [i] reply size of ReplyBuffer
//
// Output: na
//
// Notes:
//
VOID PingCallbackCommon(DWORD ioresult, sPingContext* pingcontext, DWORD replysize)
{
// parse response buffer
if (pingcontext) {
if (ioresult==IP_SUCCESS) {
if (pingcontext->fnIcmpParseReplies(pingcontext->ReplyBuffer, replysize)) {
// point to reply buffer
PICMP_ECHO_REPLY pechoreply=reinterpret_cast<PICMP_ECHO_REPLY>(pingcontext->ReplyBuffer);
if (pechoreply->Status==IP_SUCCESS) {
// check response
if (pechoreply->DataSize==sizeof(PingSignature)) {
if (memcmp(pechoreply->Data, PingSignature, pechoreply->DataSize)==0) {
// successful ping
pingcontext->Mutex->Lock();
pingcontext->IPsFound->AddItem(pechoreply->Address);
pingcontext->Mutex->Unlock();
}
}
}
}
}
// reduce count
InterlockedDecrement(pingcontext->OutstandingCount);
// clean up
delete pingcontext;
}
}
//-------------------------------------------------------------------------
// Purpose: Callback for async ping
//
// Input: apccontext - [i] context passed on ping
//
// Output: na
//
// Notes:
//
VOID PingCallbackOld(PVOID apcontext)
{
sPingContext *pingcontext=reinterpret_cast<sPingContext*>(apcontext);
PingCallbackCommon(IP_SUCCESS, pingcontext, sizeof(pingcontext->ReplyBuffer));
}
//-------------------------------------------------------------------------
// Purpose: Callback for async ping
//
// Input: apccontext - [i] context passed on ping
// iostatusblock - [i] status of request
//
// Output: na
//
// Notes:
//
VOID PingCallback(PVOID apcontext, PIO_STATUS_BLOCK iostatusblock, ULONG reserved)
{
PingCallbackCommon(iostatusblock->Status, reinterpret_cast<sPingContext*>(apcontext), iostatusblock->Information);
}
//-------------------------------------------------------------------------
// Purpose: build list of network hosts using IPv4 Ping
//
// Input: subnet - [i] subnet being scanned (LSB format)
// hoststart - [i] host starting number for scan
// hostend - [i] host ending number for scan
// ips - [io] numeric buffer to update with found addresses
//
// Output: na
//
// Notes:
//
void IPV4Ping(IPAddr sourceip, uint32_t subnet, uint32_t hoststart, uint32_t hostend, CNumericBuffer<uint32_t> &ips)
{
// skip 127. network
if ((sourceip & 0xFF)==127)
return;
bool oldlib=false;
LPFN_IcmpSendEcho2Ex fnIcmpSendEcho2Ex=NULL;
LPFN_IcmpCreateFile fnIcmpCreateFile=NULL;
LPFN_IcmpCloseHandle fnIcmpCloseHandle=NULL;
LPFN_IcmpParseReplies fnIcmpParseReplies=NULL;
// first thing is first - check which set of functions to use
HMODULE hlib=LoadLibrary(_T("iphlpapi.dll"));
if (hlib) {
// load functions
fnIcmpCreateFile=(LPFN_IcmpCreateFile) GetProcAddress(hlib, "IcmpCreateFile");
fnIcmpSendEcho2Ex=(LPFN_IcmpSendEcho2Ex) GetProcAddress(hlib, "IcmpSendEcho2Ex");
fnIcmpCloseHandle=(LPFN_IcmpCloseHandle) GetProcAddress(hlib, "IcmpCloseHandle");
fnIcmpParseReplies=(LPFN_IcmpParseReplies) GetProcAddress(hlib, "IcmpParseReplies");
}
// check if have everything
if (!hlib || fnIcmpCreateFile==NULL || fnIcmpSendEcho2Ex==NULL || fnIcmpCloseHandle==NULL || fnIcmpParseReplies==NULL) {
// no, try old version
oldlib=true;
// clean up
if (hlib) {
FreeLibrary(hlib);
}
// load old lib
hlib=LoadLibrary(_T("icmp.dll"));
// check if loaded
if (hlib) {
// load functions
fnIcmpCreateFile=(LPFN_IcmpCreateFile) GetProcAddress(hlib, "IcmpCreateFile");
fnIcmpSendEcho2Ex=(LPFN_IcmpSendEcho2Ex) GetProcAddress(hlib, "IcmpSendEcho2Ex");
fnIcmpCloseHandle=(LPFN_IcmpCloseHandle) GetProcAddress(hlib, "IcmpCloseHandle");
fnIcmpParseReplies=(LPFN_IcmpParseReplies) GetProcAddress(hlib, "IcmpParseReplies");
}
}
// check if have everything
if (hlib) {
if (fnIcmpCreateFile!=NULL && fnIcmpSendEcho2Ex!=NULL && fnIcmpCloseHandle!=NULL && fnIcmpParseReplies!=NULL) {
// open icmp
HANDLE hicmp=fnIcmpCreateFile();
if (hicmp!=INVALID_HANDLE_VALUE) {
// variables for callback handling
ULONG outstandingcount=0;
CMutex mutex;
// process pings
for (uint32_t host=hoststart; host<=hostend; host++) {
// build full ip
IPAddr ip=subnet | host;
ip=GETMSBFDWORD(&ip);
// create context
sPingContext *pcontext;
if ((pcontext=new sPingContext(&outstandingcount, &mutex, &ips, fnIcmpParseReplies))!=NULL) {
// count request
InterlockedIncrement(&outstandingcount);
// now issue ping
DWORD result=fnIcmpSendEcho2Ex(hicmp,
NULL,
oldlib ? (PIO_APC_ROUTINE) PingCallbackOld : PingCallback,
pcontext,
sourceip,
ip,
PingSignature,
sizeof(PingSignature),
NULL,
pcontext->ReplyBuffer,
sizeof(pcontext->ReplyBuffer),
50);
// check if failed
if (result==0) {
// check if because pending
if (GetLastError()!=ERROR_IO_PENDING) {
// no - use callback to clean up
CDebugPrint::DebugPrint(_T("IcmpSendEcho Error %u\n"), GetLastError());
PingCallbackOld(pcontext);
}
else {
// fire off pending APC callbacks ready
SleepEx(0, TRUE);
}
}
else {
// completed sync - use callback to clean up
PingCallbackOld(pcontext);
}
}
}
// wait for completion
while (outstandingcount) {
// handle callbacks
SleepEx(10, TRUE);
}
// clean up
fnIcmpCloseHandle(hicmp);
}
}
// clean up
FreeLibrary(hlib);
}
}
//-------------------------------------------------------------------------
// Purpose: build list of network hosts by way of IP scan for V4
//
// Input: ipadapteraddress - [i] adapter ip address to build for
//
// Output: na
//
// Notes: ip addresses are MSBF
//
void IPV4Scan(IP_ADAPTER_UNICAST_ADDRESS *ipadapteraddress)
{
// build the subnet mask to use
if (ipadapteraddress->OnLinkPrefixLength<=32 && ipadapteraddress->OnLinkPrefixLength!=0) {
in_addr ia=reinterpret_cast<sockaddr_in*>(ipadapteraddress->Address.lpSockaddr)->sin_addr;
// valid mask length - build mask
uint32_t rangemask=((1U<<(32-ipadapteraddress->OnLinkPrefixLength))-1);
uint32_t mask=~rangemask;
uint32_t subnet=GETMSBFDWORD(&ia.s_addr) & mask;
CDebugPrint::DebugPrint(_T("Subnet %u.%u.%u.%u/%u\n"), (subnet>>24) & 0xFF, (subnet>>16) & 0xFF, (subnet>>8) & 0xFF, (subnet>>0) & 0xFF, ipadapteraddress->OnLinkPrefixLength);
CDebugPrint::DebugPrint(_T("Scanning %u hosts\n"), (UINT32_MAX & rangemask)-1);
CNumericBuffer<uint32_t> ipsfound;
IPV4Ping(ia.s_addr, subnet, 1, (UINT32_MAX & rangemask)-1, ipsfound);
for (UINT i=0; i<(UINT)ipsfound.GetCount(); i++) {
uint32_t ip=ipsfound[i];
CDebugPrint::DebugPrint(_T("Ping found %u.%u.%u.%u\n"), ip & 0xFF, (ip>>8) & 0xFF, (ip>>16) & 0xFF, (ip>>24) & 0xFF);
}
}
else CDebugPrint::DebugPrint(_T("Invalid subnet length %u\n"), ipadapteraddress->OnLinkPrefixLength);
}
I presume the first one should be "if the ApcRoutine AND Event
paramters are NULL"?
yes, you correct.
But I don't see that, I see it return 0 and GetLastError() returns
ERROR_IO_PENDING. So, can both cases exist, or is the documentation
completely wrong?
documentation completely wrong. by fact IcmpSendEcho2[Ex] return BOOL and error code via SetLastError ( more exactly by RtlNtStatusToDosError)
so on asynchronous call it return FALSE (0) and GetLastError() will be ERROR_IO_PENDING if all ok - this mean apc callback will be called, or another error if fail - apc callback will be not called (better call it by self in this case, for common error handling)
how do I put the main thread in an alterable state
this already depend from what your thread doing. in some case possible write event loop with MsgWaitForMultipleObjectsEx function - at once wait on windows events and be alertable. also possible wait on some objects too. if you can not rewrite self message loop with MsgWaitForMultipleObjectsEx - you can do call from worked thread, or periodically call SleepEx(0, TRUE) or undocumented NtTestAlert. without know what your main thread doing - hard say exactly what is better.
demo code can look like:
#include <iphlpapi.h>
#include <IPExport.h>
#include <icmpapi.h>
class EchoRequestContext
{
HANDLE _hFile = 0;
PVOID _ReplyBuffer = 0;
LONG _dwRefCount = 1;
ULONG _dwThreadId = GetCurrentThreadId();
static void WINAPI sOnApc(PVOID This, PIO_STATUS_BLOCK piosb, ULONG )
{
reinterpret_cast<EchoRequestContext*>(This)->OnApc(
RtlNtStatusToDosError(piosb->Status),
(ULONG)piosb->Information);
}
void OnApc(ULONG dwError, ULONG ReplySize)
{
OnReply(dwError, (PICMP_ECHO_REPLY)_ReplyBuffer, ReplySize);
if (_ReplyBuffer) delete [] _ReplyBuffer;
Release();
}
void OnReply(ULONG dwError, PICMP_ECHO_REPLY ReplyBuffer, ULONG ReplySize)
{
if (dwError)
{
DbgPrint("dwError=%u\n", dwError);
return ;
}
if (IcmpParseReplies(ReplyBuffer, ReplySize))
{
__nop();
}
}
~EchoRequestContext()
{
if (_hFile) IcmpCloseHandle(_hFile);
PostThreadMessageW(_dwThreadId, WM_QUIT, 0, 0);
}
public:
void AddRef()
{
InterlockedIncrementNoFence(&_dwRefCount);
}
void Release()
{
if (!InterlockedDecrement(&_dwRefCount))
{
delete this;
}
}
ULONG Create()
{
HANDLE hFile = IcmpCreateFile();
if (hFile == INVALID_HANDLE_VALUE)
{
return GetLastError();
}
_hFile = hFile;
return NOERROR;
}
void SendEcho(
IPAddr DestinationAddress,
const void* RequestData,
WORD RequestSize,
ULONG ReplySize,
ULONG Timeout,
UCHAR Flags,
UCHAR Ttl)
{
if (PVOID ReplyBuffer = new UCHAR[ReplySize])
{
_ReplyBuffer = ReplyBuffer;
IP_OPTION_INFORMATION opt = { Ttl, 0, Flags };
AddRef();
ULONG dwError = IcmpSendEcho2Ex(_hFile, 0, sOnApc, this,
0, DestinationAddress,
const_cast<void*>(RequestData), RequestSize,
&opt, ReplyBuffer, ReplySize, Timeout) ? NOERROR : GetLastError();
switch (dwError)
{
case NOERROR:
case ERROR_IO_PENDING:
break;
default:
OnApc(dwError, 0 );
}
return ;
}
OnApc(ERROR_OUTOFMEMORY, 0);
}
};
#define IP(a, b, c, d) ((ULONG)(a + (b << 8) + (c << 16) + (d << 24)))
void EchoTest()
{
WSADATA wd;
if (NOERROR == WSAStartup(WINSOCK_VERSION, &wd))
{
if (EchoRequestContext* p = new EchoRequestContext)
{
if (p->Create() == NOERROR)
{
p->SendEcho(IP(8,8,8,8), "1234567890ABCDEF", 16, 0x100, 4000, IP_FLAG_DF, 255);
}
p->Release();
}
MSG msg;
__loop:
switch (MsgWaitForMultipleObjectsEx(0, 0, INFINITE,
QS_ALLINPUT, MWMO_ALERTABLE|MWMO_WAITALL))
{
default:
__debugbreak();
break;
case WAIT_FAILED:
break;
case WAIT_OBJECT_0:
while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
goto __exit;
}
}
case STATUS_USER_APC: // == WAIT_IO_COMPLETION
goto __loop;
}
__exit:
WSACleanup();
}
}

select with other objects than sockets on windows

I'm facing an issue doing a select() call waiting on a socket + pipe.
I know there are already some topics on that but I have read lots of things and their opposite and I can't figure out what is the best solution for my problem.
The best for me would be to use WaitForMultipleObjects() listening on these two objects but when I try to call it only on the WSAEvent object, it fails and last error catch is code 6 (Invalid Handle).
WSAEVENT sockEvent = WSACreateEvent();
sockEvent = WSAEventSelect(fd, sockEvent, FD_WRITE);
HANDLE *pHandles = &sockEvent;
DWORD dwEvent = WaitForMultipleObjects(1, pHandles, FALSE, amqp_time_ms_until(deadline));
switch (dwEvent)
{
// ghEvents[0] was signaled
case WAIT_OBJECT_0 + 0:
// TODO: Perform tasks required by this event
return AMQP_STATUS_OK;
// ghEvents[1] was signaled
case WAIT_OBJECT_0 + 1:
// TODO: Perform tasks required by this event
return AMQP_STATUS_POLL_EXTERNAL_WAKE;
case WAIT_TIMEOUT:
return AMQP_STATUS_TIMEOUT;
// Return value is invalid.
default:
return AMQP_STATUS_SOCKET_ERROR;
}
So WaitForMultipleObjects doesn't seems to Work with WinSocks events, however I have already seen some examples on the net working with it.
And the of WSACreateEvent documentation (https://msdn.microsoft.com/en-us/library/windows/desktop/ms741561%28v=vs.85%29.aspx) says this :
Windows Sockets 2 event objects are system objects in Windows
environments. Therefore, if a Windows application wants to use an
auto-reset event rather than a manual-reset event, the application can
call the CreateEvent function directly.
This doesn't mean that WSAEvent are based on regular windows events ? If it's the case why it doesn't work with WaitForMultipleObjects ? The doc says it can handle regular events.
Thanks for helping.
This is your problem:
sockEvent = WSAEventSelect(fd, sockEvent, FD_WRITE);
You're overwriting the event handle! (As documented, the return value for WSAEventSelect is either 0 or SOCKET_ERROR. It is not a new event handle.)
Try something like
if (WSAEventSelect(fd, sockEvent, FD_WRITE) != 0) return SOCKET_ERROR;
Looking at the declaration of WSAEVENT revealed that WSAEVENT is simply an alias for HANDLE. This explains the note of the WSACreateEvent documentation you added to your post. So WSACreateEvent simply creates a manual reset event by calling CreateEvent(..., TRUE, FALSE, ...);.
Therefore an event returned by WSACreateEvent has to work along with WaitForMultipleObjects(..).
According to the code you've posted I cannot see any reason why WaitForMultipleObjects(..) should return "invalid handle" when supplied with an event returned by WSACreateEvent...
It may be though that pipes do not work with WaitForMultipleObjects(..). I remember having problems with that a long time ago but I cannot remember the details right now. But maybe it is another place to start digging...
Here is the code of my little test application which creates two threads (one event thread signalling a normal event and a simple TCP/IP server sending data). In the main loop a connection to the server is established and signalled events are processed.
#include <winsock2.h>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#pragma comment(lib, "Ws2_32.lib");
#define SERVER_PORT 5000
HANDLE hSomeEvent;
HANDLE hSocketEvent;
DWORD WINAPI eventThread(LPVOID pData)
{
while (1)
{
SleepEx(2250, FALSE);
SetEvent(hSomeEvent);
}
return (0);
}
DWORD WINAPI serverThread(LPVOID pData)
{
SOCKET listener;
struct sockaddr_in sockaddr;
int size;
SOCKET client;
listener = socket(AF_INET, SOCK_STREAM, 0);
if (listener == INVALID_SOCKET)
{
printf("Could not create socket : %d" , WSAGetLastError());
}
sockaddr.sin_family = AF_INET;
sockaddr.sin_addr.s_addr = INADDR_ANY;
sockaddr.sin_port = htons(SERVER_PORT);
if (bind(listener, (struct sockaddr *)&sockaddr , sizeof(sockaddr)) == SOCKET_ERROR)
{
printf("Bind failed with error code : %d" , WSAGetLastError());
}
listen(listener, 1);
while (listener)
{
size = sizeof(struct sockaddr_in);
client = accept(listener, (struct sockaddr *)&sockaddr, &size);
printf("client connected\n");
while (client != INVALID_SOCKET)
{
SleepEx(5000, FALSE);
if (send(client, "hello\0", 6, 0) != 6)
{
closesocket(client);
shutdown(client, 2);
client = INVALID_SOCKET;
}
}
SetEvent(hSomeEvent);
}
return (0);
}
int main()
{
WSADATA wsaData;
HANDLE events[2];
DWORD result;
SOCKET s;
struct hostent *hp;
struct sockaddr_in sockaddr;
int len;
char buff[1024 * 16];
HANDLE *evtPtr;
WSAStartup(MAKEWORD(2, 2), &wsaData);
hSocketEvent = WSACreateEvent();
//hSocketEvent = CreateEvent(NULL, FALSE, FALSE, "socket_event");
hSomeEvent = CreateEvent(NULL, FALSE, FALSE, "some_event");
CreateThread(NULL, 0, eventThread, NULL, 0, &result);
CreateThread(NULL, 0, serverThread, NULL, 0, &result);
s = socket(AF_INET, SOCK_STREAM, 0);
if (s == INVALID_SOCKET)
{
printf("Could not create socket : %d" , WSAGetLastError());
}
hp = gethostbyname("127.0.0.1");
sockaddr.sin_addr.s_addr = *((unsigned long*)hp->h_addr);
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons(SERVER_PORT);
if (connect(s, (struct sockaddr*)&sockaddr, sizeof(sockaddr)))
{
closesocket(s);
printf("Could not connect socket : %d" , WSAGetLastError());
}
WSAEventSelect(s, hSocketEvent, FD_READ);
do
{
//events[0] = hSocketEvent;
//events[1] = hSomeEvent;
//result = WaitForMultipleObjects(2, events, FALSE, 1000);
evtPtr = &hSocketEvent;
result = WaitForMultipleObjects(1, evtPtr, FALSE, 1000);
switch (result)
{
case WAIT_OBJECT_0 + 0:
printf("hSocketEvent is signalled!\n");
len = recv(s, buff, sizeof(buff), 0);
printf(" %d bytes received\n", len);
WSAResetEvent(hSocketEvent);
break;
case WAIT_OBJECT_0 + 1:
printf("hSomeEvent is signalled!\n");
break;
case WAIT_TIMEOUT:
printf("timeout\n");
break;
default:
printf("error = %d\n", GetLastError());
break;
}
}
while (1);
printf("\n\nend.");
getch();
return (0);
}
Note that if you use WSACreateEvent you have to manually reset the event after readinng the data (otherwise WaitForMultipleObjects(..) will go nuts).

send and receive via Serial port Windows

I have a application running on windows, which will send data over serial port.
Here is the code:
m_hCommPort= ::CreateFile(L"\\\\.\\COM3",
GENERIC_READ|GENERIC_WRITE, 0, 0, OPEN_EXISTING,0,0);
if(m_hCommPort == INVALID_HANDLE_VALUE)
{
printf("COM error: %d\n", GetLastError());
}
config.DCBlength = sizeof(config);
if((GetCommState(m_hCommPort, &config) == 0))
{
CloseHandle(m_hCommPort);
printf("Get configuration port has a problem.\n");
return FALSE;
}
config.BaudRate = 9600;
config.StopBits = ONESTOPBIT;
config.Parity = PARITY_NONE;
config.ByteSize = DATABITS_8;
config.fDtrControl = 0;
config.fRtsControl = 0;
if (!SetCommState(m_hCommPort, &config))
{
CloseHandle(m_hCommPort);
printf( "Failed to Set Comm State Reason: %d\n",GetLastError());
return E_FAIL;
}
Here is the code for Send only (Working) (continuously sending )
while(1)
{
Sleep(5000);
int isWritten = WriteFile(m_hCommPort, txData, 9/*(DWORD)sizeof(txData)*/, &dwBytesWritten, NULL);
printf("isWritten: %d, dwBytesWritten: %d \n", isWritten, dwBytesWritten);
}
After this I have added code for Receive data too, then send is NOT WORKING. I mean not able to send data over UART. WriteFile() seems not executed, its stuck.
Here I have added a thread to receive data, is thread causing the problem ? or do I need to do something else ?
void ReceiverThread(void *param)
{
DWORD dwRead=0;
BOOL fWaitingOnRead = FALSE;
OVERLAPPED osReader = {0};
osReader.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (osReader.hEvent == NULL)
printf("Error creating overlapped event; abort.\n");
while(1)
{
if (!ReadFile(m_hCommPort, &Byte, 1, &dwRead, &osReader)) {
if (GetLastError() != ERROR_IO_PENDING) // read not delayed?
printf("Error in communications; report it.\n");
else
fWaitingOnRead = TRUE;
}
else {
rxData[rxHeadIndex++]= Byte;
rxHeadIndex = (rxHeadIndex) & QUEUE_MASK;
}
}
}
SetCommMask (m_hCommPort, EV_RXCHAR/ | EV_ERR); //receive character event
_beginthread(ReceiverThread,0,NULL);
while(1)
{
Sleep(5000);
int isWritten = WriteFile(m_hCommPort, txData, 9/*(DWORD)sizeof(txData)*/, &dwBytesWritten, NULL);
printf("isWritten: %d, dwBytesWritten: %d \n", isWritten, dwBytesWritten);
}
Thanks in advance.
Ashok
I've encountered a similar problem a while ago.
I found out that WriteFile(..) blocks if a ReadFile(..) is currently in progress for a serial port. So that's a problem if ReadFile(..) blocks if there is no data to be read.
I've solved the problem by checking if there is data available within the serial buffer to be read by using the function ClearCommError(..). This ensures that ReadFile(..) can read something immediately and does not unnecessarily block the device. Try changing your ReceiverThread into something like this:
void ReceiverThread(void *param)
{
DWORD dwRead=0;
COMSTAT comStat;
char buffer[1024];
while (m_hCommPort != INVALID_HANDLE_VALUE)
{
if ((ClearCommError(m_hCommPort, NULL, &comStat) != FALSE) && (comStat.cbInQue > 0))
{
/* todo: ensure buffer is big enough to contain comStat.cbInQue bytes! */
if (ReadFile(m_hCommPort, buffer, comStat.cbInQue, &dwRead, NULL) != FALSE)
{
/* do something with data in buffer */
}
}
/* avoid busy-wait */
if (comStat.cbInQue == 0)
{
SleepEx(1, FALSE);
}
}
}
This way ReadFile(..) is only called if data is available and meanwhile WriteFile(..) can send data without being blocked.
Unfortunately I've not been able to make ClearCommError(..) blocking so I used the SleepEx(1, FALSE); work-around to avoid a busy-wait and therefore prefenting the ReceiverThread to eat up the CPU.
config.fDtrControl = 0;
config.fRtsControl = 0;
These settings turn the DTR and RTS handshake lines off. Most serial devices pay attention to these signals. They won't send anything when your DTR signal is off, assuming that the machine is not powered up. And won't send anything when your RTS signal is off, assuming that the machine is not ready to receive any data.
So what you observed is entirely normal.
Since the device appears to be "normal" and does pay attention to the handshake lines, you'll want to configure the DCB to let the device driver automatically control these signals. Fix:
config.fDtrControl = DTR_CONTROL_ENABLE;
config.fRtsControl = RTS_CONTROL_HANDSHAKE;
Also the default for terminal emulators like Putty and HyperTerminal. Use such a program first to ensure that the wiring and device are functional. If you can't get any device data to show up in such a program then it won't work with your program either. If this all checks out then also set the fDsrSensitivity, fOutxCtsFlow and fOutxDsrFlow properties to TRUE so that you will, in turn, pay attention to the handshake signals of the device.

SerialPorts and WaitForMultipleObjects

I'm having some problems with serial ports in a cross-platform application (with Linux embedded and actual embedded targets), which also works on Windows to make development easier. This is about the Windows implementation.
The implementation of the serial protocol is, therefore, targetted at a mixture of OS- and non-OS systems and I won't touch the implementation itself. I'd like to make it compatible with the existing implementation. If that fails within reasonable time, I'll just make a separate thread for serial reading.
OK, basically the implementation opens the serial port, registers the file descriptor in our IO system (which uses epoll on Linux and WaitForMultipleObjects on Windows) and then, basically, just waits for all handles and does whatever required. So we want to read from the serial port when the handle is signaled for reading. Unfortunately on Windows, you can't specify if you're waiting for read or write, so I thought I'd use the following solution:
CreateFile with FILE_FLAG_OVERLAPPED
SetCommMask with EV_RXCHAR
Create an OVERLAPPED structure with a manual reset event
Call WaitCommEvent with said OVERLAPPED structure, which usually returns ERROR_IO_PENDING
That's the basic setup. I register the event handle instead of the file handle to wait on. When the handle is signalled, I do the following:
ReadFile
If successful, ResetEvent and call WaitCommEvent again
It seems, however, that if you specify FILE_FLAG_OVERLAPPED, you must use overlapped IO also for reading and writing. So I thought that whenever ReadFile or WriteFile return ERROR_IO_PENDING, I'll just wait for the IO with WaitForSingleObject and GetOverlappedResult. It seems that I don't get into that though. It seems to work basically, but sometimes it crashes on one of the ResetEvent calls, as if the overlapped was still active (though I guess it still shouldn't crash).
So, the actual question. Can this be done as I want it? Is there a problem with the approach in general, or should it work? Or is using yet another thread the only good solution? The communication is already in a separate thread, so it would be at least three threads then.
I'll try to post as much code as needed, though it is reduced from the actual code which contains a lot of things not directly related to serial reading.
SerialPort::SerialPort(const std::string &filename)
{
fd = INVALID_HANDLE_VALUE;
m_ov = new OVERLAPPED(); // Pointer because header shouldn't include Windows.h.
memset(m_ov, 0, sizeof(OVERLAPPED));
m_waitHandle = m_ov->hEvent = CreateEvent(0, true, 0, 0);
}
SerialPort::~SerialPort(void)
{
Close();
CloseHandle(m_ov->hEvent);
delete m_ov;
}
The constructor is called in a separate thread, which later calls Open:
bool SerialPort::Open(void)
{
if (fd != INVALID_HANDLE_VALUE)
return true;
fd = CreateFile(filename.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (fd != INVALID_HANDLE_VALUE) {
DCB dcb;
ZeroMemory(&dcb, sizeof(DCB));
COMMTIMEOUTS timeouts = {0};
timeouts.ReadIntervalTimeout = TimeOut();
timeouts.ReadTotalTimeoutConstant = TimeOut();
timeouts.ReadTotalTimeoutMultiplier = TimeOut() / 5;
if (timeouts.ReadTotalTimeoutMultiplier == 0) {
timeouts.ReadTotalTimeoutMultiplier = 1;
}
if (!SetCommTimeouts(fd, &timeouts)) {
DebugBreak();
}
SetCommMask(fd, EV_RXCHAR);
InitWait();
return true;
}
return false;
}
void SerialPort::InitWait()
{
if (WaitForSingleObject(m_ov->hEvent, 0) == WAIT_OBJECT_0) {
return; // Still signaled
}
DWORD dwEventMask;
if (!WaitCommEvent(fd, &dwEventMask, m_ov)) {
// For testing, I have some prints here for the different cases.
}
}
Via a rather long chain, the thread then calls WaitForMultipleObjects on m_waitHandle, which is the same as the hEvent member of the OVERLAPPED structure. This is done in a loop, and there are several other handles in the list, that's why this is different from the typical solution where you have a thread exclusively reading from the serial port. I have, basically, no control about the loop, that's why I try to do the WaitCommEvent (within InitWait) at just the right time.
When the handle is signaled, the ReadData method is called by the thread:
int SerialPort::ReadData(void *buffer, int size)
{
if (fd != INVALID_HANDLE_VALUE) {
// Timeouts are reset here to MAXDWORD/0/0, not sure if necessary.
DWORD dwBytesRead;
OVERLAPPED ovRead = {0};
ovRead.hEvent = CreateEvent(0, true, 0, 0);
if (ReadFile(fd, buffer, size, &dwBytesRead, &ovRead)) {
if (WaitForSingleObject(m_ov->hEvent, 0) == WAIT_OBJECT_0) {
// Only reset if signaled, because we might get here because of a timer.
ResetEvent(m_waitHandle);
InitWait();
}
CloseHandle(ovRead.hEvent);
return dwBytesRead;
} else {
if (GetLastError() == ERROR_IO_PENDING) {
WaitForSingleObject(ovRead.hEvent, INFINITE);
GetOverlappedResult(fd, &ovRead, &dwBytesRead, true);
InitWait();
CloseHandle(ovRead.hEvent);
return dwBytesRead;
}
}
InitWait();
CloseHandle(ovRead.hEvent);
return -1;
} else {
return 0;
}
}
The write is done as follows, without syncing:
int SerialPort::WriteData(const void *buffer, int size)
{
if (fd != INVALID_HANDLE_VALUE) {
DWORD dwBytesWritten;
OVERLAPPED ovWrite = {0};
ovWrite.hEvent = CreateEvent(0, true, 0, 0);
if (!WriteFile(fd, buffer, size, &dwBytesWritten, &ovWrite)) {
if (GetLastError() == ERROR_IO_PENDING) {
WaitForSingleObject(ovWrite.hEvent, INFINITE);
GetOverlappedResult(fd, &ovWrite, &dwBytesWritten, true);
CloseHandle(ovWrite.hEvent);
return dwBytesWritten;
} else {
CloseHandle(ovWrite.hEvent);
return -1;
}
}
CloseHandle(ovWrite.hEvent);
}
return 0;
}
It seems that it does work now. There are no crashes anymore, at least I can't reproduce them. So as it works now, I'm just asking if what I do is sane, or if I should do things differently.
Offhand, I don't see any errors in the code you have shown, but I would like to suggest alternative code to clean up your error handling in ReadData() and WriteData() in general:
int SerialPort::ReadData(void *buffer, int size)
{
if (fd == INVALID_HANDLE_VALUE)
return 0;
OVERLAPPED ovRead = {0};
ovRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!ovRead.hEvent)
return -1;
DWORD dwBytesRead;
if (!ReadFile(fd, buffer, size, &dwBytesRead, &ovRead))
{
if (GetLastError() != ERROR_IO_PENDING)
{
CloseHandle(ovRead.hEvent);
return -1;
}
if (!GetOverlappedResult(fd, &ovRead, &dwBytesRead, TRUE))
{
CloseHandle(ovRead.hEvent);
return -1;
}
}
if (WaitForSingleObject(m_waitHandle, 0) == WAIT_OBJECT_0)
{
ResetEvent(m_waitHandle);
InitWait();
}
CloseHandle(ovRead.hEvent);
return dwBytesRead;
}
int SerialPort::WriteData(const void *buffer, int size)
{
if (fd == INVALID_HANDLE_VALUE)
return 0;
OVERLAPPED ovWrite = {0};
ovWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!ovWrite.hEvent)
return -1;
DWORD dwBytesWritten;
if (!WriteFile(fd, buffer, size, &dwBytesWritten, &ovWrite))
{
if (GetLastError() != ERROR_IO_PENDING)
{
CloseHandle(ovWrite.hEvent);
return -1;
}
if (!GetOverlappedResult(fd, &ovWrite, &dwBytesWritten, TRUE))
{
CloseHandle(ovWrite.hEvent);
return -1;
}
}
CloseHandle(ovWrite.hEvent);
return dwBytesWritten;
}

Does marshaling a marshaled interface give me a marshaller to the proxy or the original interface?

Here is a concrete example:
I create a IWeBrowser2 interface by calling wb.CoCreateInstance(CLSID_InternetExplorer, 0, CLSCTX_SERVER);. This gives me a marshaled interface from my process into whichever of the running iexplore.exe processes happens to contain this browser tab in my thread A.
Now I use the IGlobalInterfaceTable to get a cookie for this interface, pass it to my thread B and request the marshaled interface from there.
Question: Do I get a proxy to the proxy in my thread A or directly to the instance in the IE process?
It seems sensible to me that I will get a direct proxy to the instance with its own reference to it, however:
If I end my thread A, the cookie I created there becomes invalid and I can't retrieve (and close) the interface pointers to the web browsers I created any more. This does not make sense unless there is a thunk in that thread that is destroyed when the thread quits.
Edit: Oh, both threads are STA.
I finally had some time to figure out what is happening, so I wrote a short test to see what is going on.
// MarshalTest.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
enum { WM_THEREYOUGO = WM_USER+1, WM_THANKYOU, WM_YOURWELCOME };
DWORD WINAPI TheOtherThread(DWORD * main_thread_id)
{
MSG msg = { 0 };
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
assert(SUCCEEDED(hr));
{
// create web browser
CComPtr<IWebBrowser2> wb;
hr = wb.CoCreateInstance(CLSID_InternetExplorer, 0, CLSCTX_SERVER);
assert(SUCCEEDED(hr) && wb);
// navigate
hr = wb->Navigate2(&CComVariant(_T("stackoverflow.com")), &CComVariant(0), &CComVariant(_T("")), &CComVariant(), &CComVariant());
assert(SUCCEEDED(hr));
hr = wb->put_Visible(VARIANT_TRUE);
assert(SUCCEEDED(hr));
// Marshal
DWORD the_cookie = 0;
{
CComPtr<IGlobalInterfaceTable> com_broker;
hr = com_broker.CoCreateInstance(CLSID_StdGlobalInterfaceTable);
assert(SUCCEEDED(hr));
hr = com_broker->RegisterInterfaceInGlobal(wb, __uuidof(IWebBrowser2), &the_cookie);
}
// notify main thread
PostThreadMessage(*main_thread_id, WM_THEREYOUGO, the_cookie, NULL);
// message loop
while(GetMessage(&msg, 0, 0, 0)) {
if(msg.hwnd == NULL) {
// thread message
switch(msg.message) {
case WM_THANKYOU:
PostQuitMessage(0);
break;
}
} else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
CoUninitialize();
PostThreadMessage(*main_thread_id, WM_YOURWELCOME, 0, NULL);
return msg.wParam;
}
int _tmain(int argc, _TCHAR* argv[])
{
MSG msg = {0};
DWORD main_thread_id = GetCurrentThreadId();
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
assert(SUCCEEDED(hr));
{
DWORD ThreadId = 0;
HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)TheOtherThread, &main_thread_id, 0, &ThreadId);
DWORD the_cookie = 0;
CComPtr<IWebBrowser2> wb, wb2;
while(GetMessage(&msg, 0, 0, 0)) {
if(msg.hwnd == NULL) {
// thread message
switch(msg.message) {
case WM_THEREYOUGO:
// we got the cookie.
the_cookie = msg.wParam;
// get the browser. This should work.
{
CComPtr<IGlobalInterfaceTable> com_broker;
hr = com_broker.CoCreateInstance(CLSID_StdGlobalInterfaceTable);
assert(SUCCEEDED(hr));
hr = com_broker->GetInterfaceFromGlobal(the_cookie, __uuidof(IWebBrowser2), (void**)&wb);
assert(SUCCEEDED(hr) && wb);
}
// do something with it.
hr = wb->put_FullScreen(VARIANT_TRUE);
assert(SUCCEEDED(hr));
// signal the other thread.
PostThreadMessage(ThreadId, WM_THANKYOU, 0, NULL);
break;
case WM_YOURWELCOME:
// the other thread has ended.
PostQuitMessage(0);
break;
}
} else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// the other thread has ended. Try getting the interface again.
{
CComPtr<IGlobalInterfaceTable> com_broker;
hr = com_broker.CoCreateInstance(CLSID_StdGlobalInterfaceTable);
assert(SUCCEEDED(hr));
hr = com_broker->GetInterfaceFromGlobal(the_cookie, __uuidof(IWebBrowser2), (void**)&wb2);
//assert(SUCCEEDED(hr) && wb2); // this fails, hr == E_INVALIDARG.
// clean up, will not be executed.
if(SUCCEEDED(hr)) {
hr = com_broker->RevokeInterfaceFromGlobal(the_cookie);
}
}
// try using it
if(wb2) {
hr = wb2->put_FullScreen(VARIANT_FALSE);
assert(SUCCEEDED(hr));
} else if(wb) {
// this succeeds
hr = wb->put_FullScreen(VARIANT_FALSE);
assert(SUCCEEDED(hr));
}
CloseHandle(hThread);
}
CoUninitialize();
return msg.wParam;
}
The bottom line is this:
Ending the thread that registered the interface invalidates the cookie.
The already marshaled interface stays valid. (In this case, that is.)
This means that I get a proxy to the IE process instead of to the other thread's object.
You already got a proxy on thread A since you asked for an out-of-process server. What happens next depends on the kind of apartment that thread A lives in, the argument to CoInitializeEx(). If it is MTA you will definitely get the same proxy in thread B, assuming it is MTA as well. The added reference count should keep it alive if Thread A exits. If it is STA then I'm not 100% sure but think you ought to get a new one. Easy to test btw, just use the one from thread A and you'll get RPC_E_WRONGTHREAD if a new one would have to be created.
I don't have a great explanation for why the thread A exit kills the proxy for thread B. Unless you call IGlobalInterfaceTable::RevokeInterfaceFromGlobal(). Which you'd normally do.

Resources