How can I accomplish the next behavior(more specific to implement callMint) in Yul? I couldn't find anything related to this
contract Token is ERC20{
....
function mint(address addr, uint256 amount) external {
_mint(addr, amount);
}
....
}
struct Param{
address owner;
uint256 amount;
}
contract Test{
....
function callMint(address tokenAddress, Param param) external {
Token(tokenAddress).mint(param.owner, param.amount);
}
....
}
Basically you need to copy the calldata for the external function you intend to do into memory. In your case the calldata is the abi.encode of the selector (4 bytes) and the two arguments.
I haven't tested it, but it should look like this:
function callMint(address tokenAddress, Param param) external {
address _owner = param.owner;
uint256 _amount = param.amount;
bytes4 _selector = 0x40c10f19; // bytes4(keccak('mint(address,uint256)'))
assembly {
// We need 0x44 bytes for the calldata in memory.
// We use the free memory pointer to find where to save it.
let calldataOffset := mload(0x40)
// We move the free memory pointer, but if this function is
// 'external' we don't really need to
mstore(0x40, add(calldataOffset, 0x44))
// Store salldata to memory
mstore(calldataOffset, _selector)
mstore(add(calldataOffset, 0x04), _owner)
mstore(add(calldataOffset, 0x24), _amount)
let result := call(
gas(),
tokenAddress,
0, // msg.value
calldataOffset,
0x44,
0, // return data offset
0 // return data length
)
// Revert if call failed
if eq(result, 0) {
// Forward the error
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
}
}
Related
func Encode(p SomeStruct) (buf []byte, err error) {
var bs bytes.Buffer
if err = json.NewEncoder(&bs).Encode(p); err != nil {
return
}
in := C.CString(bs.String())
defer C.free(unsafe.Pointer(in)) //Is this necessary?
res := make([]byte, 1024)
out := C.CString(string(res))
defer C.free(unsafe.Pointer(out)) //and this one
res := C.encode(in, out, 1024) //int encode(char *, void *, int)
//... ...
}
Question: shall I use C.free() to ensure that the code does not leak memory? I feel that this is not required as the variable is allocated by Go, not C, right?
My question is, will CString always allocate memory or, it will do that only if necessary?
// Go string to C string
// The C string is allocated in the C heap using malloc.
// It is the caller's responsibility to arrange for it to be
// freed, such as by calling C.free (be sure to include stdlib.h
// if C.free is needed).
func C.CString(string) *C.char
As stated in the documentation, you need release the memory manually. The CString function (_Cfunc_CString in cmd/cgo/out.go) actually calls malloc from C standard library and the programmer is supposed to manage this memory. The returned string can be deallocated with C.free in the Go side or with free inside the C code.
I am trying to dump mem rd/wr trace for a specific function call from my application and after researching a bit I came across a solution to do so.
But since I am very new to PIN usage, I am not sure how to pass routine names (refer to Routine(RTN rtn, VOID *v)) from application to pin tools so that the right callback function gets trigerred. Can someone please help?
As of now If I run the given pin tools, my trace.out is empty because "!isROI" is always set to false.
#include <stdio.h>
#include "pin.H"
#include <string>
const CHAR * ROI_BEGIN = "__parsec_roi_begin";
const CHAR * ROI_END = "__parsec_roi_end";
FILE * trace;
bool isROI = false;
// Print a memory read record
VOID RecordMemRead(VOID * ip, VOID * addr, CHAR * rtn)
{
// Return if not in ROI
if(!isROI)
{
return;
}
// Log memory access in CSV
fprintf(trace,"%p,R,%p,%s\n", ip, addr, rtn);
}
// Print a memory write record
VOID RecordMemWrite(VOID * ip, VOID * addr, CHAR * rtn)
{
// Return if not in ROI
if(!isROI)
{
return;
}
// Log memory access in CSV
fprintf(trace,"%p,W,%p,%s\n", ip, addr, rtn);
}
// Set ROI flag
VOID StartROI()
{
isROI = true;
}
// Set ROI flag
VOID StopROI()
{
isROI = false;
}
// Is called for every instruction and instruments reads and writes
VOID Instruction(INS ins, VOID *v)
{
// Instruments memory accesses using a predicated call, i.e.
// the instrumentation is called iff the instruction will actually be executed.
//
// On the IA-32 and Intel(R) 64 architectures conditional moves and REP
// prefixed instructions appear as predicated instructions in Pin.
UINT32 memOperands = INS_MemoryOperandCount(ins);
// Iterate over each memory operand of the instruction.
for (UINT32 memOp = 0; memOp < memOperands; memOp++)
{
// Get routine name if valid
const CHAR * name = "invalid";
if(RTN_Valid(INS_Rtn(ins)))
{
name = RTN_Name(INS_Rtn(ins)).c_str();
}
if (INS_MemoryOperandIsRead(ins, memOp))
{
INS_InsertPredicatedCall(
ins, IPOINT_BEFORE, (AFUNPTR)RecordMemRead,
IARG_INST_PTR,
IARG_MEMORYOP_EA, memOp,
IARG_ADDRINT, name,
IARG_END);
}
// Note that in some architectures a single memory operand can be
// both read and written (for instance incl (%eax) on IA-32)
// In that case we instrument it once for read and once for write.
if (INS_MemoryOperandIsWritten(ins, memOp))
{
INS_InsertPredicatedCall(
ins, IPOINT_BEFORE, (AFUNPTR)RecordMemWrite,
IARG_INST_PTR,
IARG_MEMORYOP_EA, memOp,
IARG_ADDRINT, name,
IARG_END);
}
}
}
// Pin calls this function every time a new rtn is executed
VOID Routine(RTN rtn, VOID *v)
{
// Get routine name
const CHAR * name = RTN_Name(rtn).c_str();
if(strcmp(name,ROI_BEGIN) == 0) {
// Start tracing after ROI begin exec
RTN_Open(rtn);
RTN_InsertCall(rtn, IPOINT_AFTER, (AFUNPTR)StartROI, IARG_END);
RTN_Close(rtn);
} else if (strcmp(name,ROI_END) == 0) {
// Stop tracing before ROI end exec
RTN_Open(rtn);
RTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)StopROI, IARG_END);
RTN_Close(rtn);
}
}
// Pin calls this function at the end
VOID Fini(INT32 code, VOID *v)
{
fclose(trace);
}
/* ===================================================================== */
/* Print Help Message */
/* ===================================================================== */
INT32 Usage()
{
PIN_ERROR( "This Pintool prints a trace of memory addresses\n"
+ KNOB_BASE::StringKnobSummary() + "\n");
return -1;
}
/* ===================================================================== */
/* Main */
/* ===================================================================== */
int main(int argc, char *argv[])
{
// Initialize symbol table code, needed for rtn instrumentation
PIN_InitSymbols();
// Usage
if (PIN_Init(argc, argv)) return Usage();
// Open trace file and write header
trace = fopen("roitrace.csv", "w");
fprintf(trace,"pc,rw,addr,rtn\n");
// Add instrument functions
RTN_AddInstrumentFunction(Routine, 0);
INS_AddInstrumentFunction(Instruction, 0);
PIN_AddFiniFunction(Fini, 0);
// Never returns
PIN_StartProgram();
return 0;
}
We have a Windows app that we're adding a socket interface for remote configuration and data processing. A TIdTCPServer object receives the messages in the OnExecute event. For some messages, however, the OnExecute event is not fired unless the cursor is moved in the main window.
UPDATE: With a lot more experimenting, whether or not the message is processed immediately, or after a long pause, or not at all, seems more random. In all cases, moving the cursor causes the message to be processed immediately. However, it doesn't seem to be specific to a message, or the order of messages.
Updated source code listings: Here's the OnExecute handler:
void __fastcall TSigToolForm::IdTCPServer1Execute(TIdContext *AContext)
{
TIdBytes buffer;
if (ReceiveBuffer(AContext, buffer))
{
try
{
try
{
msg = &buffer[0]; // msg is class member
TThread::Synchronize(0, mProcess); // Doesn't return until mProcess finishes
buffer = IPOK().toByteArray(); // Ack
SendBuffer(AContext, buffer);
}
catch (const std::exception & ex)
{
buffer = IPFailCommand(ex.what()).toByteArray();
SendBuffer(AContext, buffer);
}
catch (const Exception & ex)
{
buffer = IPFailCommand(toStdString(ex.Message)).toByteArray();
SendBuffer(AContext, buffer);
}
catch (const EIdException & ex)
{
throw; // Let Indy have it
}
}
__finally
{
msg = 0;
}
}
}
The mProcess function and the processMessage function it calls. I stripped all but one message type that processMessage handles:
void __fastcall TSigToolForm::mProcess()
{
if (msg) processMessage(msg);
}
void TSigToolForm::processMessage(byte * message)
{
CSLock lock(cs); // RAII class, cs is TCriticalSection
try
{
IPCommand cmd(message);
switch (cmd.ID)
{
case IPCommand::SET_CAD :
{
setObjectCad(cmd);
break;
}
}
}
catch(const std::exception & ex)
{
ShowMessage(ex.what());
}
catch (const EIdException & ex)
{
throw;
}
catch (...)
{
ShowMessage("Exception in processMessage");
}
}
The ReceiveBuffer and SendBuffer functions:
bool ReceiveBuffer(TIdTCPClient * aClient, TIdBytes & ABuffer)
{
return ReceiveBuffer(aClient->IOHandler, ABuffer);
}
bool ReceiveBuffer(TIdContext * AContext, TIdBytes & ABuffer)
{
return ReceiveBuffer(AContext->Connection->IOHandler, ABuffer);
}
bool ReceiveBuffer(TIdIOHandler * IO, TIdBytes & ABuffer)
{
CSLock lock(cs);
try
{
long sz = IO->ReadLongInt();
IO->ReadBytes(ABuffer, sz, false);
return true;
}
catch (const EIdException & ex)
{
throw;
}
return false;
}
bool SendBuffer(TIdIOHandler * IO, const TIdBytes & ABuffer)
{
CSLock lock(cs);
try
{
IO->WriteBufferOpen();
try
{
IO->Write(ABuffer.Length);
IO->Write(ABuffer);
IO->WriteBufferClose();
}
catch(const Exception &)
{
IO->WriteBufferCancel();
throw;
}
}
catch(const EIdException &)
{
throw;
}
catch (...)
{
return false;
}
return true;
}
bool SendBuffer(TIdContext * AContext, const TIdBytes & ABuffer)
{
return SendBuffer(AContext->Connection->IOHandler, ABuffer);
}
bool SendBuffer(TIdTCPClient * aClient, const TIdBytes & aBuffer)
{
return SendBuffer(aClient->IOHandler, aBuffer);
}
For testing I have a separate program that creates and send the various messages, using TIdTCPClient and the same send / receive buffer functions. This is the only connection to the server program. Here's an example:
void TForm16::setPortAndConnect()
{
IdTCPClient1->Port = bdePort->IntValue();
IdTCPClient1->Host = editHost->Text;
IdTCPClient1->Connect();
}
void TForm16::sendCommandToSVST(const TIdBytes & buffer)
{
try
{
setPortAndConnect();
if (SendBuffer(IdTCPClient1, buffer))
{
TIdBytes recv;
//
// Read the response
if (ReceiveBuffer(IdTCPClient1, recv))
{
IPCommand response = IPCommand::fromByteArray(recv);
}
}
}
__finally
{
IdTCPClient1->Disconnect();
}
}
bdePort is an internal TEdit-derived that deals with numerical input. I'm confident that the data itself is correct. It's getting the server to respond that is a problem right now.
I assume at this point that there must be something the program itself is doing that's interfering with the GUI thread or the socket connection or both. I know this is very open-ended, but any hints on what to look for would be appreciated.
This is C++Builder 10.1 update 1, using the classic compiler.
For some messages, however, the OnExecute event is not fired unless the cursor is moved in the main window.
TIdTCPServer is a multi-threaded component, the OnExecute event is fired in a worker thread in a continuous loop for the lifetime of the socket connection. So the ONLY way it could be getting blocked until mouse activity is detected is if your OnExecute code is synchronizing with the main UI thread, and the main UI thread is blocked until window messages are received.
In the code you have shown, the only places where your OnExecute code could be getting blocked are the calls to ReceiveBuffer(), mProcess(), and SendBuffer(). Make sure they are all thread-safe. You did not show the code for any of those methods, or the code for your main UI thread, but mProcess() is being called via TThread::Synchronize() so start with that one and make sure your main UI thread is not blocking mProcess() while it is trying to process a socket message.
BTW, you are catching only STL-based exceptions (derived from std::exception), but you are completely ignoring RTL-based exceptions (derived from System::Sysutils::Exception). And in the case of Indy-based exceptions (which are derived from EIdException, which itself is derived from System::Sysutils::Exception), DO NOT swallow them! If you catch an Indy exception, re-throw it and let TIdTCPServer handle it, otherwise its threads will not be able to detect socket disconnects and clean up properly (unless you manually call AContext->Connection->Disconnect() in your code).
Don't know the Indy version, whatever came with the compiler.
You can find out the Indy version by:
looking for Indy in the IDE's "About" box
right-clicking on any Indy component in the Form Designer at design-time.
reading the Version property of any Indy component at runtime.
UPDATE: Why are you using a critical section around everything? You don't need that.
You are reading/writing a client socket from only 1 thread (the one firing the OnExecute event). Even if you were reading in one thread and writing in another thread, that is safe to do with sockets without placing a lock around the IOHandler. So you don't need a lock around those IOHandler operations at all.
And your mProcess() method is already being serialized by TThread::Synchronize(), so it will only ever run in the main UI thread. If multiple client threads want to call mProcess() at the same time, Synchronize() ensures it runs only one at a time. So you don't need a lock for that, either. However, your use of ShowMessage() inside of mProcess() is problematic, because it runs a secondary message loop that will allow pending Synchronize() requests to run while mProcess() is still running, so you can end up with multiple mProcess() calls overlapping each other. You should not be doing anything inside of a synced method that can cause window messages to be processed. If a synced method throws an exception, you should not try to catch it. Synchronize() catches exceptions and rethrows them in the context of the thread that called Synchronize(), and you already have exception handlers in your OnExecute code.
The only place I see where you should be using any kind of lock, if any at all, would be inside of setObjectCad(), but only if it needs to access data that can be accessed by multiple threads at the same time.
With that said, try something more like this instead:
void ReceiveBuffer(TIdTCPClient * aClient, TIdBytes & ABuffer)
{
ReceiveBuffer(aClient->IOHandler, ABuffer);
}
bool ReceiveBuffer(TIdContext * AContext, TIdBytes & ABuffer)
{
ReceiveBuffer(AContext->Connection->IOHandler, ABuffer);
}
void ReceiveBuffer(TIdIOHandler * IO, TIdBytes & ABuffer)
{
long sz = IO->ReadLongInt();
IO->ReadBytes(ABuffer, sz, false);
}
void SendBuffer(TIdIOHandler * IO, const TIdBytes & ABuffer)
{
IO->WriteBufferOpen();
try
{
IO->Write(ABuffer.Length);
IO->Write(ABuffer);
IO->WriteBufferClose();
}
catch(const Exception &)
{
IO->WriteBufferCancel();
throw;
}
}
void SendBuffer(TIdContext * AContext, const TIdBytes & ABuffer)
{
SendBuffer(AContext->Connection->IOHandler, ABuffer);
}
void SendBuffer(TIdTCPClient * aClient, const TIdBytes & aBuffer)
{
SendBuffer(aClient->IOHandler, aBuffer);
}
void __fastcall TSigToolForm::IdTCPServer1Execute(TIdContext *AContext)
{
TIdBytes buffer;
ReceiveBuffer(AContext, buffer);
try
{
msg = &buffer[0]; // msg is class member
TThread::Synchronize(0, mProcess); // Doesn't return until mProcess finishes
buffer = IPOK().toByteArray(); // Ack
SendBuffer(AContext, buffer);
}
catch (const std::exception & ex)
{
buffer = IPFailCommand(ex.what()).toByteArray();
SendBuffer(AContext, buffer);
}
catch (const Exception & ex)
{
buffer = IPFailCommand(toStdString(ex.Message)).toByteArray();
SendBuffer(AContext, buffer);
if (dynamic_cast<const EIdException *>(&ex))
throw;
}
catch (...)
{
buffer = IPFailCommand("Unknown exception").toByteArray();
SendBuffer(AContext, buffer);
}
}
void __fastcall TSigToolForm::mProcess()
{
if (msg) processMessage(msg);
}
void TSigToolForm::processMessage(byte * message)
{
IPCommand cmd(message);
switch (cmd.ID)
{
case IPCommand::SET_CAD :
{
setObjectCad(cmd);
break;
}
}
}
void TSigToolForm::setObjectCad(const IPCommand &cmd)
{
// here is where you should be using CSLock, if at all...
}
void TForm16::setPortAndConnect()
{
IdTCPClient1->Port = bdePort->IntValue();
IdTCPClient1->Host = editHost->Text;
IdTCPClient1->Connect();
}
void TForm16::sendCommandToSVST(const TIdBytes & buffer)
{
setPortAndConnect();
try
{
// Send the command
SendBuffer(IdTCPClient1, buffer);
// Read the response
TIdBytes recv;
ReceiveBuffer(IdTCPClient1, recv);
IPCommand response = IPCommand::fromByteArray(recv);
}
__finally
{
IdTCPClient1->Disconnect();
}
}
I am a newbie working with Indy. This is my first time posting a question here.
My project has to send data to all clients at 60Hz. I am using TIdTCPServer for this, and to monitor keep-alives. My tool is very old, running on WinXP, using C++Builder 6 and Indy 8. There is a potential timeout issue, does anyone have a good thought how to handle it?
Here is my code:
Server Side
typedef struct
{
AnsiString PeerIP; //{ Cleint IP address }
AnsiString HostName; //{ Hostname }
int Id; // {Cleint ID}
} TClient;
// This is Multimedia timer callback function, on 60Hz
void CALLBACK mmTimerProc(UINT wTimerID, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2)
{
DWORD T1, T2;
TfmMain *pMain = (TfmMain *)dwUser;
int Index;
double dT;
TClient *pClient;
if (pMain->IsClosing) return;
if (pMain->Shutdown)
{
return;
}
pMain->UpdateServer1Data();
TList *pList = pMain->IdTCPServer1->Threads->LockList();
try
{
for(int X = 0; X < pList->Count; X++)
{
if (!pMain->IsClosing)
{
TIdPeerThread *AThread = (TIdPeerThread *)pList->Items[X];
if(AThread != NULL)
{
pClient = (TClient *)AThread->Data;
try
{
if(!AThread->Connection->ClosedGracefully)
{
// Send data to ALL Clients
AThread->Connection->WriteBuffer(&pMain->Data2Client, sizeof(pMain->Data2Client), true);
}
}
catch(Exception &E)
{
if(!AThread->Stopped)
{
AThread->Stop();
AThread->Connection->Disconnect();
}
}
}
}
}
}
__finally
{
pMain->IdTCPServer1->Threads->UnlockList();
}
// Shutdown computer or close application
if(pMain->SimIos.Status.iSimStatus == 11)
{
pMain->Shutdown = true;
pMain->CloseApp();
}
}
void __fastcall TfmMain::IdTCPServer1Connect(TIdPeerThread *AThread)
{
TClient *pClient = NULL;
AnsiString ABuffer, text;
AnsiString PeerIP = AThread->Connection->Binding->PeerIP;
TDateTime TimeConnected = Now();
ABuffer = AThread->Connection->ReadLn();
if((ABuffer.Pos("TT") == 0) && (ABuffer.Pos("IG") == 0) && (ABuffer.Pos("RR") == 0))
{
text = AnsiString().sprintf("1>>> Unknown(%s) on %s connected illegal!...",
PeerIP, DateTimeToStr(TimeConnected));
WriteMsg(text);
AThread->Connection->Disconnect();
return;
}
if(ABuffer.Pos("IG") != 0)
{
pClient = new TClient;
pClient->PeerIP = PeerIP;
pClient->HostName = Clients[eIG];
pClient->Id = eIG;
AThread->Data = (TObject *)pClient;
AThread->FreeOnTerminate = true;
// Report client is on line
}
text = AnsiString().sprintf("1>>>%s(%s) on %s on line!...",
pClient->HostName, PeerIP, DateTimeToStr(TimeConnected));
WriteMsg(text);
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::IdTCPServer1Disconnect(TIdPeerThread *AThread)
{
TClient *pClient = NULL;
AnsiString Msg;
if (IsClosing) return;
pClient = (TClient *)AThread->Data;
if(pClient->Id == 1)
{
// Report client is off line
Msg = AnsiString().sprintf("1>>>%s(%s) on %s off line...",
pClient->HostName, pClient->PeerIP, DateTimeToStr(Now()));
WriteMsg(Msg);
}
delete pClient;
AThread->Data = NULL;
AThread->Terminate();
try
{
IdTCPServer1->Threads->LockList()->Remove(AThread);
}
__finally
{
IdTCPServer1->Threads->UnlockList();
}
}
//---------------------------------------------------------------------------
void __fastcall TfmMain::IdTCPServer1Execute(TIdPeerThread *AThread)
{
TClient *pClient;
if (!AThread->Terminated && !IsClosing)
{
pClient = (TClient *)AThread->Data;
if((pClient != NULL) && (pClient->Id != 0))
{
try
{
if(pClient->Id == 1)
{
// Report client still alive
}
}
catch(Exception &E)
{
if (!IsClosing)
{
if(!AThread->Stopped)
{
AThread->Stop();
AThread->Connection->Disconnect();
}
}
}
}
}
}
Client side
void __fastcall TSocketThread::Execute()
{
unsigned long ulCheckSum;
SIM_SVR1_ACK_STRUCT Ack2Svr;
SIM_SVR_DATA DataFromSvr;
int Counter;
memset(&DataFromSvr, 0, sizeof(DataFromSvr));
memset(&Ack2Svr, 0, sizeof(Ack2Svr));
Counter = 0;
// fetch and process commands until the connection or thread is terminated
while (!this->Terminated && FSocket->Connected())
{
try
{
// recieve data from server
FSocket->ReadBuffer(&DataFromSvr, sizeof(DataFromSvr));
// check CRC
ulCheckSum = CRC_32((unsigned char*)&DataFromSvr.SimData, sizeof(DataFromSvr.SimData));
if (ulCheckSum == DataFromSvr.uiCheckSum)
{
FSmIpcUtil->Writeto(&DataFromSvr.SimData, DATA_OFFSET, sizeof(DataFromSvr.SimData));
}
else
{
// counter to record error
Synchronize(UpdateCaption);
}
// read return from local SM
FSmIpcUtil->Readfrom(&Ack2Svr, ACK_OFFSET, sizeof(Ack2Svr));
FSocket->WriteBuffer(&Ack2Svr, sizeof(Ack2Svr));
if (DataFromSvr.SimData.SimIgTgt.Acdata.iSimStatus == 11)
{
Terminate();
FSocket->Disconnect();
PostMessage(Application->Handle, WM_SHUTDOWN, 0, 0);
Sleep(500);
}
}
catch (EIdException& E)
{
this->Terminate();
FSocket->Disconnect();
}
}
}
There are several issues with your code.
A multimedia timer callback is very restricted in what it is allowed to do:
Applications should not call any system-defined functions from inside a callback function, except for PostMessage, timeGetSystemTime, timeGetTime, timeSetEvent, timeKillEvent, midiOutShortMsg, midiOutLongMsg, and OutputDebugString.
If transmission speed is important, don't have the timer callback do the broadcasting at all. Save the data somewhere safe, and then have each TIdTCPServer thread grab the latest data on its own time. This also keeps each connection thread isolated so one connection cannot affect any other connection if problems occur.
DO NOT set the TIdPeerThread::FreeOnTerminate to true, DO NOT call TIdPeerThread::Stop(), DO NOT manually remove threads from the TIdTCPServer::Threads property. You do not own the threads, TIdTCPServer does, and it will manage them for you. If you want to stop a given thread, closing the thread's socket is all you need to do. But by moving all of the sending logic into OnExecute where it belongs, you can let TIdTCPServer handle any errors and close the socket for you.
Your OnConnect event handler is setting AThread->Data only if an IG client connects, but your OnConnect and OnDisconnect handlers are not checking for that condition before attempting to access the TClient object.
Your OnDisconnect event handler is leaking memory if IsClosing is true. And it is calling Threads->UnlockList() without calling Threads->LockList() first. Attempting to unlock the list when it is not locked by the calling thread will cause errors and deadlocks.
Try something more like this:
struct TClient
{
AnsiString PeerIP; //{ Client IP address }
AnsiString HostName; //{ Hostname }
int Id; //{ Client ID }
};
void CALLBACK mmTimerProc(UINT wTimerID, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2)
{
TfmMain *pMain = (TfmMain *)dwUser;
if (pMain->IsClosing || pMain->Shutdown) return;
pMain->UpdateServer1Data();
// make sure pMain->Data2Client is thread-safe...
// set a signal that Data2Client has been updated...
// Shutdown computer or close application
if (pMain->SimIos.Status.iSimStatus == 11)
{
pMain->Shutdown = true;
pMain->CloseApp();
}
}
void __fastcall TfmMain::IdTCPServer1Connect(TIdPeerThread *AThread)
{
TClient *pClient;
AnsiString ABuffer, text;
AnsiString PeerIP = AThread->Connection->Binding->PeerIP;
TDateTime TimeConnected = Now();
ABuffer = AThread->Connection->ReadLn();
if ((ABuffer.Pos("TT") == 0) && (ABuffer.Pos("IG") == 0) && (ABuffer.Pos("RR") == 0))
{
text = AnsiString().sprintf("1>>> Unknown(%s) on %s connected illegal!...", PeerIP.c_str(), DateTimeToStr(TimeConnected).c_str());
WriteMsg(text);
AThread->Connection->Disconnect();
return;
}
pClient = new TClient;
pClient->PeerIP = PeerIP;
if (ABuffer.Pos("IG") != 0)
{
pClient->HostName = Clients[eIG];
pClient->Id = eIG;
}
else
pClient->Id = 0;
AThread->Data = (TObject *)pClient;
// Report client is on line
text = AnsiString().sprintf("1>>>%s(%s) on %s on line!...", pClient->HostName.c_str(), PeerIP.c_str(), DateTimeToStr(TimeConnected).c_str());
WriteMsg(text);
}
void __fastcall TfmMain::IdTCPServer1Disconnect(TIdPeerThread *AThread)
{
TClient *pClient = (TClient *)AThread->Data;
AnsiString Msg;
AThread->Data = NULL;
if (pClient)
{
// Report client is off line
Msg = AnsiString().sprintf("1>>>%s(%s) on %s off line...",
pClient->HostName.c_str(), pClient->PeerIP.c_str(), DateTimeToStr(Now()).c_str());
WriteMsg(Msg);
delete pClient;
}
}
void __fastcall TfmMain::IdTCPServer1Execute(TIdPeerThread *AThread)
{
TClient *pClient;
if (IsClosing) return;
// make sure pMain->Data2Client is thread-safe...
if (Data2Client has been updated since last event)
{
AThread->Connection->WriteBuffer(&pMain->Data2Client, sizeof(pMain->Data2Client), true);
}
pClient = (TClient *)AThread->Data;
// Report client still alive
}
In the following code
SendMessage() is the API called by the user to send a message over USB
UsbDataReceived() is the function called by another thread when data is received on the USB
Task() is the thread created in init() that reads from the queue filled by SendMessage() and calls the low level usb function to send a message
UsbConnected() is the function called by another thread when a USB device is connected.
I need to implement the following behaviour:
Once a message has been sent, no new messages can be sent up to when an answer for the last message has arrived
When UsbConnected() is called, all of the queues must be emptied and if the thread Task() is waiting on the queue AnswerQueue, it has to be unblocked so that it can go back to wait on the queue CommandQueue
An example of my best solution is listed below.
I had to post an EVENT_QUEUE_RESETTED message on both of the queues with a particular order.
Is it possible to achieve the same result in a less cumbersome way?
void UsbhOut(Message_t );
int UsbConnected;
QueueHandle_t MessageQueue;
typedef enum
{
EVENT_QUEUE_RESETTED,
EVENT_NEW_MESSAGE,
EVENT_NEW_ANSWER,
} Event_t;
typedef struct
{
Event_t Event;
uint8_t Data[32];
} CommandQueue_t;
typedef struct
{
Event_t Event;
uint8_t Data[32];
} AnswerQueue_t;
void init(void)
{
MessageQueue = xQueueCreate(sizeof(Message_t), 10);
AnswerQueue = xQueueCreate(sizeof(Message_t), 10);
xTaskCreate(Task, "", 200, NULL, 1, NULL);
}
void UsbConnected(void)
{
CommandEvent.Event = EVENT_QUEUE_RESETTED;
AnswerEvent.Event = EVENT_QUEUE_RESETTED;
UsbConnected = 1;
xQueueReset(MessageQueue);
xQueueReset(AnswerQueue);
/* The order here is fundmental */
xQueueSend(AnswerQueue, &AnswerEvent, 0);
xQueueSend(MessageQeueue, &CommandEvent, 0);
}
int SendMessage(uint8_t * Data)
{
CommandQueue_t CommandEvent;
CommandEvent.Event = EVENT_NEW_MESSAGE;
memcpy(&CommandEvent, Message, 32);
if(xQueueSend(MessageQueue, &CommandEvent, 0) == pdTRUE)
return 0;
else
return -1;
}
void UsbDataReceived(uint8_t * Data)
{
AnswerQueue_t AnswerEvent;
AnswerEvent.Event = EVENT_NEW_ANSWER;
memcpy(AnswerEvent.Data, Data, 32);
xQueueSend(AnswerQueue, &AnswerEvent,0);
}
void Task(void *pvParameters)
{
CommandQueue_t CommandEvent;
AnswerQueue_t AnswerEvent;
CommandCallback_t * Callback;
while(1)
{
xQueueReceive(UsbhStaticData.CommandSenderQueue, &CommandEvent, portMAX_DELAY);
if(CommandEvent.Event == CommandSenderQueueData_t::EVENT_QUEUE_RESETTED)
{
continue;
}
/* Low level Usb fucntion used to send Data */
UsbdSendData(Command.Data);
xQueueReceive(UsbhStaticData.AnswerReceivdQueue, &AnswerEvent, portMAX_DELAY);
if(AnswerEvent.Event == AnswerReceivedQueueData_t::EVENT_QUEUE_RESETTED)
{
continue;
}
else if(AnswerEvent.Event == AnswerReceivedQueueData_t::EVENT_ANSWER_RECEIVED)
{
ParseData(AnswerEvent.Data);
}
}
}
You could use Queue Sets
FreeRTOS Queue Set API
That way you could create a Queue Set containing a queue and a semaphore. Your task would block on the Queue Set. It would then get unblocked by either receiving a message from the queue or you incrementing the semaphore.