I am trying to create a JNI wrapper for this windows application called 'Personal Communications' through a COM Object that it supplies. PCOM Help
Through lots of googling, I have managed alot on my own. I can suffessfully connect to the COM Object and run getter/setter methods to retrieve various info on the application.
My problem is now with Sink Objects (trying to get the application to send me events).
As far as I can tell, everything is returning a good return code, but the Invoke on the Sink Class is never called. Whats worse, if a Event was suppose to be called, Releasing the COM object that told it to start sending events, hangs the application. If I end the application advising it of the sink but not making it fire any events, the application doesn't hang.
I have tried various methods and compilers. They all give the same result. Whats weird though, if I use the same code in an actual application (exe) outside of Java, everything works fine and the Events are fired through the Sink Object. So I am truly at a lost for whats wrong with Java in the picture.
Here is my code I have so far:
PcommControl.h (My Wrapper for controlling the COMs)
initConnectionManagerEvents: creates a new sink object and advises the COM of it. RegisterStartEvent tells the COM to start sending messages to the sink objects.
removeConnectionManagerEvents: unadvises and removes all sink objects. UnregisterStartEvent tells the COM to stop sending messages to the sink objects.
jobject PcommControl::initConnectionManagerEvents(jobject sinkType) {
if (!initConnectionManager())
return NULL;
if (autConnectionManagerPoint == NULL) {
autConnectionManagerPoint = getConnectionPoint(autConnectionManager,
DIID_IStartEvent);
if (autConnectionManagerPoint == NULL)
return NULL;
}
ConnectionSink *conSinkC = new ConnectionSink(jvm, sinkType);
if (!conSinkC->isInitialized()) {
delete conSinkC;
return NULL;
}
if (!conSinkC->Advise(autConnectionManagerPoint)) {
#ifdef DEBUG
printf("PcommControl: failed to advise ConnectionManagerEvent\n");
#endif
delete conSinkC;
return NULL;
}
#ifdef DEBUG
printf("PcommControl: ConnectionManagerEvent> %ld\n", conSinkC->getCookie());
#endif
ConnectionSink *temp = connectionManagerEvents.put(
(long) conSinkC->getCookie(), conSinkC);
if (temp) {
temp->Release();
}
if (connectionManagerEvents.getSize() == 1) {
#ifdef DEBUG
printf("PcommControl: Registering ConnectionManagerEvent\n");
#endif
HRESULT hresult = autConnectionManager->RegisterStartEvent();
if (!SUCCEEDED(hresult)) {
#ifdef DEBUG
printf("Failed to get RegisterStartEvent\n");
#endif
// TODO
}
}
return conSinkC->getJavaObjectConnection();
}
void PcommControl::removeConnectionManagerEvents() {
ConnectionSink *connectionSink;
if ((autConnectionManager) && (!safeUnload)) {
#ifdef DEBUG
printf("PcommControl: Unregistering ConnectionManagerEvent\n");
#endif
// TODO: seems to cause hanging issues for Java
HRESULT hresult = autConnectionManager->UnregisterStartEvent();
if (!SUCCEEDED(hresult)) {
#ifdef DEBUG
printf("Failed to UnregisterStartEvent\n");
#endif
}
}
while ((connectionSink = connectionManagerEvents.removeHead()) != NULL) {
if (!safeUnload) {
#ifdef DEBUG
printf("PcommControl: releasing a connection manager event\n");
#endif
connectionSink->Unadvise();
#ifdef DEBUG
printf("PcommControl: start release\n");
#endif
connectionSink->Release();
}
}
#ifdef DEBUG
printf("PcommControl: done releasing ConnectionManager events\n");
#endif
}
JNIEventSink.h (My Sink Interface for all JNI Sinks)
#ifndef JNIEVENTSINK_H_
#define JNIEVENTSINK_H_
#include <jni.h>
#include <OCIdl.h>
#define FARFAR FAR* FAR*
class JNIEventSink: public IDispatch {
protected:
private:
bool initialized;
bool deconstructor;
DWORD referenceCount;
JavaVM *jvm;
jobject javaObjectConnection;
DWORD cookie;
IConnectionPoint *point;
static jclass javaLangClass;
static jmethodID javaLangClassNewInstance;
void init(JavaVM *javaVM, jobject sinkType) {
initialized = false;
deconstructor = false;
referenceCount = 0;
jvm = javaVM;
javaObjectConnection = NULL;
cookie = 0;
AddRef();
// create Java sink class from sinkType
// if (javaVM) {
// JNIEnv *env;
// javaVM->AttachCurrentThread((void **) &env, NULL);
// if (env == NULL) {
//#ifdef DEBUG
// printf("JNIEventSink: java environment not found!\n");
//#endif
// return;
// }
//
// if (javaLangClass == NULL) {
// javaLangClass = NULL;
// javaLangClassNewInstance = NULL;
//
// javaLangClass = env->FindClass("java/lang/Class");
// if (javaLangClass == NULL) {
//#ifdef DEBUG
// printf("JNIEventSink: javaLangClass not found!\n");
//#endif
// return;
// }
// javaLangClassNewInstance = env->GetMethodID(javaLangClass,
// "newInstance", "()Ljava/lang/Object;");
// if (javaLangClassNewInstance == NULL) {
//#ifdef DEBUG
// printf(
// "JNIEventSink: javaLangClass NewInstance not found!\n");
//#endif
// return;
// }
// }
//
// javaObjectConnection = env->CallObjectMethod(sinkType,
// javaLangClassNewInstance);
// if (javaObjectConnection == NULL) {
//#ifdef DEBUG
// printf(
// "JNIEventSink: Failed to create new Connection Object!\n");
//#endif
// return;
// }
// }
initialized = true;
}
public:
bool test;
JNIEventSink(JavaVM *javaVM, jobject sinkType) {
#ifdef DEBUG
printf("JNIEventSink: constructor\n");
#endif
init(javaVM, sinkType);
test = false;
}
virtual ~JNIEventSink() {
#ifdef DEBUG
printf("JNIEventSink: deconstructor\n");
if (test)
printf("YESYESYESYESYESYESYESYES\n");
#endif
deconstructor = true;
//
// if (point != NULL)
// Unadvise();
//
// if (referenceCount > 0)
// Release();
}
bool isInitialized() {
return initialized;
}
bool Advise(IConnectionPoint *point) {
#ifdef DEBUG
printf("JNIEventSink: Start Advise\n");
#endif
this->point = point;
this->point->AddRef();
HRESULT hresult = point->Advise(this, &cookie);
// TODO set cookie to java class
#ifdef DEBUG
printf("JNIEventSink: Advise End\n");
if (!SUCCEEDED(hresult))
printf("JNIEventSink: failed\n");
#endif
return SUCCEEDED(hresult);
}
bool Unadvise() {
#ifdef DEBUG
printf("JNIEventSink: Start Unadvise\n");
#endif
if (point == NULL)
return true;
IConnectionPoint *point = this->point;
this->point = NULL;
HRESULT hresult = point->Unadvise(cookie);
point->Release();
#ifdef DEBUG
printf("JNIEventSink: Unadvise End\n");
if (!SUCCEEDED(hresult))
printf("JNIEventSink: failed\n");
#endif
return SUCCEEDED(hresult);
}
DWORD getCookie() {
return cookie;
}
jobject getJavaObjectConnection() {
return javaObjectConnection;
}
ULONG
STDMETHODCALLTYPE AddRef() {
#ifdef DEBUG
printf("JNIEventSink: Add Ref %ld,%ld,%ld\n", (long) this, cookie,
referenceCount);
#endif
referenceCount++;
return referenceCount;
}
ULONG
STDMETHODCALLTYPE Release() {
#ifdef DEBUG
printf("JNIEventSink: Start Release %ld,%ld,%ld\n", (long) this,
cookie, referenceCount);
#endif
if (referenceCount == 0) {
#ifdef DEBUG
printf("0 ref\n");
#endif
return 0;
}
referenceCount--;
long temp = referenceCount;
if ((temp == 0) && (!deconstructor)) {
#ifdef DEBUG
printf("JNIEventSink: deleting\n");
#endif
delete this;
}
return temp;
}
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid,
void **ppvObject) {
#ifdef DEBUG
printf("JNIEventSink: QueryInterface %ld,%ld\n", (long) this, cookie);
#endif
if (iid == IID_IUnknown) {
*ppvObject = (IUnknown *) this;
} else if (iid == IID_IDispatch) {
*ppvObject = (IDispatch *) this;
} else {
*ppvObject = (void *) this;
}
((IUnknown *) (*ppvObject))->AddRef();
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount(UINT *pctinfo) {
#ifdef DEBUG
printf("JNIEventSink: GetTypeInfoCount %ld,%ld\n", (long) this, cookie);
#endif
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames(REFIID riid,
LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) {
#ifdef DEBUG
printf("JNIEventSink: GetIDsOfNames %ld,%ld\n", (long) this, cookie);
#endif
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(unsigned int iTInfo,
LCID lcid, ITypeInfo FARFAR ppTInfo) {
#ifdef DEBUG
printf("JNIEventSink: GetTypeInfo %ld,%ld\n", (long) this, cookie);
#endif
return E_NOTIMPL;
}
// virtual HRESULT STDMETHODCALLTYPE Invoke(DISPID dispIdMember, REFIID riid,
// LCID lcid, WORD wFlags, DISPPARAMS FAR* pDispParams,
// VARIANT FAR* pVarResult, EXCEPINFO FAR* pExcepInfo,
// unsigned int FAR* puArgErr) = 0;
};
jclass JNIEventSink::javaLangClass = NULL;
jmethodID JNIEventSink::javaLangClassNewInstance = NULL;
#endif /* JNIEVENTSINK_H_ */
ConnectionSink.h (My Implementation of the JNI Sink that is suppose to get all Events from autConnectionManager)
#ifndef CONNECTIONSINK_H_
#define CONNECTIONSINK_H_
#include <jni.h>
#include "PcommInterfaces.h"
#include "..\COM\JNIEventSink.h"
class ConnectionSink: public JNIEventSink {
private:
public:
ConnectionSink(JavaVM *javaVM, jobject sinkType) :
JNIEventSink(javaVM, sinkType) {
}
virtual ~ConnectionSink() {
#ifdef DEBUG
printf("ConnectionSink: deconstructor\n");
#endif
}
// IStartEvent
// future events I want to call
// 1 - void NotifyStartEvent(VARIANT ConnHandle, VARIANT_BOOL bStarted);
// 2 - void NotifyStartError(VARIANT ConnHandle);
// 3 - void NotifyStartStop(int* Reason);
virtual HRESULT STDMETHODCALLTYPE Invoke(DISPID dispIdMember, REFIID riid,
LCID lcid, WORD wFlags, DISPPARAMS FAR* pDispParams,
VARIANT FAR* pVarResult, EXCEPINFO FAR* pExcepInfo,
unsigned int FAR* puArgErr) {
// TODO this seems like it is never called
#ifdef DEBUG
printf("ConnectionSink: Invoke %ld,%ld,%ld\n", (long) this,
this->getCookie(), dispIdMember);
#endif
test = true;
return S_OK;
//return E_NOTIMPL;
}
};
#endif /* CONNECTIONSINK_H_ */
Example output when it hangs:
Java_gov_ssa_utils_pcomm_ConnectionManager_registerStartEvents
JNIEventSink: constructor
JNIEventSink: Add Ref 72880304,0,0
JNIEventSink: Start Advise
JNIEventSink: Add Ref 72880304,0,1
JNIEventSink: Advise End
PcommControl: ConnectionManagerEvent> 1
PcommControl: Registering ConnectionManagerEvent
Created and attached sink.
Waiting 10 seconds for user to fire event
Destroying.
Java_gov_ssa_utils_PComm_release
PcommControl: destroying
PcommControl: Unregistering ConnectionManagerEvent
Thanks for any help.
I figured out my problem.
The thread that created the sink object needs to also pump the thread's message queue using the window's function 'GetMessage'.
This is why the windows executable was working correctly, it already have a message pump for the window that was created. I figured this out when I put a breakpoint in the sink object when the invoke was being called and looked at the stack trace. 'GetMessage' was the first thing on the trace that came from my code.
In this instance, I believe why 'GetMessage' is the answer is because I believe the COM's code is calling 'PostThreadMessage' to notify me that a sink event has occurred and 'GetMessage' looks at these messages and somehow knows they are COM related and handle them on its own.
PS: Notice I said the thread that created the sink. If you create the sink in one thread and have the message pump in another, you will receive the message from the COM about the sink, but it won't know how to handle it automatically since this thread has nothing loaded about the COM in its space. So the sink object's invoke is never called.
So Java could access the same COM everywhere, I made the 2nd thread control everything about the COM, and any native methods that were called redirect the request to the that thread. So no matter what thread Java is running in, it will always have access to the same COM without having to reload everything.
Related
I'm writing a DLL in C++Builder XE6, that creates a separate thread (derived from TThread) to retrieve JSON data from a REST server every X seconds (using TIdHTTP), and parse the JSON data.
The thread fills a simple struct (no dynamically allocated data) with the parsed JSON data in the Execute() method of the thread:
typedef struct
{
char MyString[40 + 1];
double MyDouble;
bool MyBool;
} TMyStruct;
The thread should store the struct in a list, for example a std::vector:
#include <vector>
std::vector<TMyStruct> MyList;
The thread will add a TMyStruct to the list:
TMyStruct Data;
...
MyList.push_back(Data);
The list will be guarded by a TCriticalSection to prevent data corruption.
The DLL exports a function to retrieve a TMyStruct from MyList.
bool __declspec(dllexport) __stdcall GetMyStruct (int Index, TMyStruct* Data)
{
...
}
Only thing is, I don't know where to put MyList...
If I make MyList a global variable, it is located in the main thread's memory and GetMyStruct() can access it directly. How does the thread access MyList?
If I make MyList a member of the TThread-derived class, it is located in the thread's memory and the thread can access it directly. How does GetMyStruct() access MyList?
What is the best/prefered/common way to store MyList and access it in a different thread?
If I make MyList a global variable, it is located in the main thread's memory and GetMyStruct() can access it directly. How does the thread access MyList?
The exact same way. All threads in a process can freely access global variables within that process. For example:
#include <vector>
#include <System.SyncObjs.hpp>
typedef struct
{
char MyString[40 + 1];
double MyDouble;
bool MyBool;
} TMyStruct;
std::vector<TMyStruct> MyList;
TCriticalSection *Lock = NULL; // why not std::mutex instead?
class TMyThread : public TThread
{
...
};
TMyThread *Thread = NULL;
...
void __fastcall TMyThread::Execute()
{
TMyStruct Data;
...
Lock->Enter();
try {
MyList.push_back(Data);
}
__finally {
Lock->Leave();
}
...
}
...
void __declspec(dllexport) __stdcall StartThread ()
{
Lock = new TCriticalSection;
Thread = new TMyThread;
}
void __declspec(dllexport) __stdcall StopThread ()
{
if (Thread) {
Thread->Terminate();
Thread->WaitFor();
delete Thread;
Thread = NULL;
}
if (Lock) {
delete Lock;
Lock = NULL;
}
}
bool __declspec(dllexport) __stdcall GetMyStruct (int Index, TMyStruct* Data)
{
if (!(Lock && Thread)) return false;
Lock->Enter();
try {
*Data = MyList[Index];
}
__finally {
Lock->Leave();
}
return true;
}
If I make MyList a member of the TThread-derived class, it is located in the thread's memory and the thread can access it directly. How does GetMyStruct() access MyList?
By accessing it via a pointer to the thread object. For example:
#include <vector>
#include <System.SyncObjs.hpp>
typedef struct
{
char MyString[40 + 1];
double MyDouble;
bool MyBool;
} TMyStruct;
class TMyThread : public TThread
{
protected:
void __fastcall Execute();
public:
__fastcall TMyThread();
__fastcall ~TMyThread();
std::vector<TMyStruct> MyList;
TCriticalSection *Lock;
};
TMyThread *Thread = NULL;
...
__fastcall TMyThread::TMyThread()
: TThread(false)
{
Lock = new TCriticalSection;
}
__fastcall TMyThread::~TMyThread()
{
delete Lock;
}
void __fastcall TMyThread::Execute()
{
TMyStruct Data;
...
Lock->Enter();
try {
MyList.push_back(Data);
}
__finally {
Lock->Leave();
}
...
}
void __declspec(dllexport) __stdcall StartThread ()
{
Thread = new TMyThread;
}
void __declspec(dllexport) __stdcall StopThread ()
{
if (Thread) {
Thread->Terminate();
Thread->WaitFor();
delete Thread;
Thread = NULL;
}
}
bool __declspec(dllexport) __stdcall GetMyStruct (int Index, TMyStruct* Data)
{
if (!Thread) return false;
Thread->Lock->Enter();
try {
*Data = Thread->MyList[Index];
}
__finally {
Thread->Lock->Leave();
}
return true;
}
What is the best/prefered/common way to store MyList and access it in a different thread?
That is entirely up to you to decide, based on your particular needs and project design.
I want to use timer interrupter combined with BLE to transmit in esp32.
I found that an error occurs when the transmission cycle is too fast.
I don't know what caused this error.
Help me plz.............................................................................................
The error
This is my code
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
////////////////////////////////////////////////////////////
#include <BLE2902.h>
#define PERIOD_MS 5
#define BAUD_RATE 115200
#define BLE_WR_EN 0x5A
#define BLE_WR_DIS 0x55
#define SERVICE_UUID "0000ffe0-0000-1000-8000-00805f9b34fb"
#define CHARACTERISTIC_UUID_RX "0000ffe2-0000-1000-8000-00805f9b34fb"
#define CHARACTERISTIC_UUID_TX "0000ffe1-0000-1000-8000-00805f9b34fb"
int PERIOD_US=PERIOD_MS*1000;
/* create a hardware timer */
hw_timer_t * timer = NULL;
/* LED pin */
int led = 2;
/* LED state */
volatile byte state = LOW;
int i=0;
BLEServer *pServer;
BLEService *pService;
BLECharacteristic *pCharacteristic;
void IRAM_ATTR onTimer(){
//state = !state;
if(i<700){
i++;
}
else{
i=0;
}
//Serial.println(state);
char txString[8];
dtostrf(i, 1, 2, txString);
pCharacteristic->setValue(txString);
pCharacteristic->notify();
std::string txValue=pCharacteristic->getValue();
Serial.print("txValue:");
Serial.println(txValue.c_str());
//digitalWrite(led, state);
}
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
Serial.println("********************************");
Serial.println("Connected");
Serial.println("********************************");
timerAlarmEnable(timer);
};
void onDisconnect(BLEServer* pServer) {
Serial.println("********************************");
Serial.println("Disconnected");
Serial.println("********************************");
timerAlarmDisable(timer);
//delay(1000);
///*
// Start the service
//pService->start();
// Start advertising
//pServer->getAdvertising()->start();
pServer->startAdvertising();
//*/
//ESP.restart();
}
};
class MyCallbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
std::string rxValue = pCharacteristic->getValue();
std::string EN="Z";
std::string DIS="U";
Serial.print("rxValue:");
Serial.println(rxValue.c_str());
/*
//Serial.println(getvalue);
if (rxValue==EN ) {
//g_Timer.enable(g_TimerId);
timerAlarmEnable(timer);
digitalWrite(LED, HIGH);
}
else if (rxValue==DIS) {
//g_Timer.disable(g_TimerId);
timerAlarmDisable(timer);
//data_count = 0;
digitalWrite(LED, LOW);
}
*/
}
};
void setup() {
Serial.begin(115200);
Serial.begin(BAUD_RATE);
////////////////////////////////////////
Serial.println("Starting BLE Server!");
BLEDevice::init("ESP32-INTERRUT-Server");
/////////////////////////////////////////////////////////////////////////////////////
BLEServer *pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
// Create the BLE Service
BLEService *pService = pServer->createService(SERVICE_UUID);
// Create a BLE Characteristic
pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_TX,
BLECharacteristic::PROPERTY_READ|
BLECharacteristic::PROPERTY_NOTIFY
);
pCharacteristic->addDescriptor(new BLE2902());
BLECharacteristic *pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_RX,
BLECharacteristic::PROPERTY_WRITE
);
pCharacteristic->setCallbacks(new MyCallbacks());
// Start the service
pService->start();
// Start advertising
pServer->getAdvertising()->start();
////////////////////////////////////////////////////////////////////
timer = timerBegin(0, 80, true);
timerAttachInterrupt(timer, &onTimer, true);
timerAlarmWrite(timer, PERIOD_US, true);
timerAlarmDisable(timer);
}
void loop() {
}
I have a function that takes a callback, and used it to do work on 10 separate threads. However, it is often the case that not all of the work is needed. For example, if the desired result is obtained on the third thread, it should stop all work being done on of the remaining alive threads.
This answer here suggests that it is not possible unless you have the callback functions take an additional std::atomic_bool argument, that signals whether the function should terminate prematurely.
This solution does not work for me. The workers are spun up inside a base class, and the whole point of this base class is to abstract away details of multithreading. How can I do this? I am anticipating that I will have to ditch std::async for something more involved.
#include <iostream>
#include <future>
#include <vector>
class ABC{
public:
std::vector<std::future<int> > m_results;
ABC() {};
~ABC(){};
virtual int callback(int a) = 0;
void doStuffWithCallBack();
};
void ABC::doStuffWithCallBack(){
// start working
for(int i = 0; i < 10; ++i)
m_results.push_back(std::async(&ABC::callback, this, i));
// analyze results and cancel all threads when you get the 1
for(int j = 0; j < 10; ++j){
double foo = m_results[j].get();
if ( foo == 1){
break; // but threads continue running
}
}
std::cout << m_results[9].get() << " <- this shouldn't have ever been computed\n";
}
class Derived : public ABC {
public:
Derived() : ABC() {};
~Derived() {};
int callback(int a){
std::cout << a << "!\n";
if (a == 3)
return 1;
else
return 0;
};
};
int main(int argc, char **argv)
{
Derived myObj;
myObj.doStuffWithCallBack();
return 0;
}
I'll just say that this should probably not be a part of a 'normal' program, since it could leak resources and/or leave your program in an unstable state, but in the interest of science...
If you have control of the thread loop, and you don't mind using platform features, you could inject an exception into the thread. With posix you can use signals for this, on Windows you would have to use SetThreadContext(). Though the exception will generally unwind the stack and call destructors, your thread may be in a system call or other 'non-exception safe place' when the exception occurs.
Disclaimer: I only have Linux at the moment, so I did not test the Windows code.
#if defined(_WIN32)
# define ITS_WINDOWS
#else
# define ITS_POSIX
#endif
#if defined(ITS_POSIX)
#include <signal.h>
#endif
void throw_exception() throw(std::string())
{
throw std::string();
}
void init_exceptions()
{
volatile int i = 0;
if (i)
throw_exception();
}
bool abort_thread(std::thread &t)
{
#if defined(ITS_WINDOWS)
bool bSuccess = false;
HANDLE h = t.native_handle();
if (INVALID_HANDLE_VALUE == h)
return false;
if (INFINITE == SuspendThread(h))
return false;
CONTEXT ctx;
ctx.ContextFlags = CONTEXT_CONTROL;
if (GetThreadContext(h, &ctx))
{
#if defined( _WIN64 )
ctx.Rip = (DWORD)(DWORD_PTR)throw_exception;
#else
ctx.Eip = (DWORD)(DWORD_PTR)throw_exception;
#endif
bSuccess = SetThreadContext(h, &ctx) ? true : false;
}
ResumeThread(h);
return bSuccess;
#elif defined(ITS_POSIX)
pthread_kill(t.native_handle(), SIGUSR2);
#endif
return false;
}
#if defined(ITS_POSIX)
void worker_thread_sig(int sig)
{
if(SIGUSR2 == sig)
throw std::string();
}
#endif
void init_threads()
{
#if defined(ITS_POSIX)
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = worker_thread_sig;
sigaction(SIGUSR2, &sa, 0);
#endif
}
class tracker
{
public:
tracker() { printf("tracker()\n"); }
~tracker() { printf("~tracker()\n"); }
};
int main(int argc, char *argv[])
{
init_threads();
printf("main: starting thread...\n");
std::thread t([]()
{
try
{
tracker a;
init_exceptions();
printf("thread: started...\n");
std::this_thread::sleep_for(std::chrono::minutes(1000));
printf("thread: stopping...\n");
}
catch(std::string s)
{
printf("thread: exception caught...\n");
}
});
printf("main: sleeping...\n");
std::this_thread::sleep_for(std::chrono::seconds(2));
printf("main: aborting...\n");
abort_thread(t);
printf("main: joining...\n");
t.join();
printf("main: exiting...\n");
return 0;
}
Output:
main: starting thread...
main: sleeping...
tracker()
thread: started...
main: aborting...
main: joining...
~tracker()
thread: exception caught...
main: exiting...
In a namespace extension, I'm creating a thread and passing in a file path to the thread function.
The problem I'm seeing is the first character of the file path gets corrupted. D:\temp0.csv gets passed in and in the thread function it becomes Y:\temp0.csv or some other random corrupted first wchar. In Win2k8R2 and Win10 it was working fine, but sometimes it would fail in the same way. I tried disabling optimizations to no avail.
The fileName variable is populated from the IShellItem that came from the IFileOpenDialog.
What do I need to do to fix this?
Caller of thread function:
LPWSTR filePath = NULL;
IFileOpenDialog *ofd = NULL;
IShellItem *file = NULL;
hrPath = file->GetDisplayName(SIGDN_FILESYSPATH, &filePath);
CreateThread(NULL, 0, CCsv::BuildTree, static_cast<LPVOID>(filePath), 0, NULL);
static class thread function
DWORD WINAPI CCsv::BuildTree(LPVOID lpParam) {
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
LPWSTR filePath = static_cast<LPWSTR>(lpParam);
}
Here is a minimal program, but it does not repro with this code. One difference is I added a wait for the thread function. THat doesn't exist in the namespace extension.
main.cpp
// buf.cpp : Defines the entry point for the console application.
//
#pragma once
#include "stdafx.h"
using std::wstring;
static CRITICAL_SECTION g_TreeLock;
static CRITICAL_SECTION g_MountQueueLock;
class CCsv
{
public:
CCsv();
~CCsv();
static DWORD WINAPI BuildTree(LPVOID lpParam);
};
class CMountPath {
public:
CMountPath();
~CMountPath();
BOOL Mount();
BOOL PathExists(LPWSTR path);
private:
CSimpleArray<wstring> m_MountQueue;
};
extern CCsv g_Csv;
CCsv::CCsv() {
InitializeCriticalSection(&g_TreeLock);
}
CCsv::~CCsv() {
DeleteCriticalSection(&g_TreeLock);
}
DWORD WINAPI CCsv::BuildTree(LPVOID lpParam) {
LPWSTR name = static_cast<LPWSTR>(lpParam);
MessageBox(NULL, name, L"", MB_OK);
CoTaskMemFree(name);
return 0;
}
CMountPath::CMountPath() {
InitializeCriticalSection(&g_MountQueueLock);
}
CMountPath::~CMountPath() {
DeleteCriticalSection(&g_MountQueueLock);
}
BOOL CMountPath::PathExists(LPWSTR path) {
return FALSE;
}
BOOL CMountPath::Mount() {
IEnumIDList *idl = NULL;
LPITEMIDLIST pidl = NULL;
LPITEMIDLIST desktopPidl = NULL;
LPCITEMIDLIST pidlRelative = NULL;
BOOL success = FALSE;
HRESULT hr, hrPath = S_FALSE;
LPWSTR filePath = NULL;
PWSTR filePathHeap = NULL;
WCHAR msg[MAXPATH+MAXMSG] = {0};
IFileOpenDialog *ofd = NULL;
IShellItem *file = NULL;
DWORD idx = 0;
BOOL isQueued = FALSE;
const COMDLG_FILTERSPEC fileSpec[] = {
{ L"CSV Text Files", L"*.csv" },
{ L"All Files", L"*.*" },
};
hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&ofd));
if (SUCCEEDED(hr)) {
if (SUCCEEDED(hr)){
ofd->SetTitle(L"Choose file");
ofd->SetFileTypes(ARRAYSIZE(fileSpec), fileSpec);
hr = ofd->Show(NULL);
if(SUCCEEDED(hr))
hr = ofd->GetResult(&file);
if(SUCCEEDED(hr))
hrPath = file->GetDisplayName(SIGDN_FILESYSPATH, &filePath);
if(SUCCEEDED(hrPath)){
LPWSTR filePathHeap = (LPWSTR)CoTaskMemAlloc(MAXPATH * sizeof(WCHAR));
if(filePathHeap) {
StringCchCopy(filePathHeap, MAXPATH, filePath);
if(PathExists(filePathHeap)) {
StringCchPrintf(msg, MAXPATH+MAXMSG, L"The file %s is already loaded.", filePathHeap);
MessageBox(NULL, msg, L"appname", MB_OK);
}
else {
EnterCriticalSection(&g_MountQueueLock);
isQueued = !m_MountQueue.Find(wstring(filePathHeap)) ? TRUE : FALSE;
if(!isQueued)
m_MountQueue.Add(wstring(filePathHeap));
LeaveCriticalSection(&g_MountQueueLock);
if(!isQueued) {
HANDLE hThread = CreateThread(NULL, 0, CCsv::BuildTree, static_cast<LPVOID>(filePathHeap), 0, NULL);
// there is no wait in the namespace extension. the wait is just to keep the console app main thread running.
if(INVALID_HANDLE_VALUE != hThread)
WaitForSingleObject(hThread, INFINITE);
}
else {
StringCchPrintf(msg, MAXPATH+MAXMSG, L"The file %s is already being loaded.", filePathHeap);
MessageBox(NULL, msg, L"appname", MB_OK);
}
}
}
CoTaskMemFree(filePath);
file->Release();
}
}
ofd->Release();
}
return success;
}
int main() {
CoInitialize(NULL);
CMountPath m;
m.Mount();
CoUninitialize();
return 0;
}
stdafx.h
#pragma once
#define MAXPATH 32767
#define MAXMSG 128
#define WIN32_LEAN_AND_MEAN
#define WINVER 0x0600
#define _WIN32_WINNT 0x0600
#include "targetver.h"
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <atlbase.h>
#include <atlstr.h>
#include <atlcoll.h>
#include <shlobj.h>
#include <Shobjidl.h>
#include <ShlGuid.h>
#include <shellapi.h>
#include <OleAuto.h>
#include <shlwapi.h>
#include <strsafe.h>
#include <string>
Why are you using threads at all? When you spawn a new thread, you are blocking your code waiting for the thread to terminate before continuing, so you are serializing all of your code. You may as well not even use threads at all.
Also, you have memory leaks and various logic errors in your code.
Try this instead:
// buf.cpp : Defines the entry point for the console application.
//
#pragma once
#include "stdafx.h"
using std::wstring;
class CCsv
{
public:
CCsv();
~CCsv();
void BuildTree(LPCWSTR name);
private:
CRITICAL_SECTION m_TreeLock;
};
class CMountPath {
public:
CMountPath();
~CMountPath();
BOOL Mount();
BOOL PathExists(LPCWSTR path);
private:
CSimpleArray<wstring> m_MountQueue;
CRITICAL_SECTION m_MountQueueLock;
};
CCsv g_Csv;
CCsv::CCsv() {
InitializeCriticalSection(&m_TreeLock);
}
CCsv::~CCsv() {
DeleteCriticalSection(&m_TreeLock);
}
void CCsv::BuildTree(LPCWSTR name) {
MessageBoxW(NULL, name, L"", MB_OK);
}
CMountPath::CMountPath() {
InitializeCriticalSection(&m_MountQueueLock);
}
CMountPath::~CMountPath() {
DeleteCriticalSection(&m_MountQueueLock);
}
BOOL CMountPath::PathExists(LPCWSTR path) {
return FALSE;
}
BOOL CMountPath::Mount() {
BOOL success = FALSE;
HRESULT hr = S_FALSE;
LPWSTR filePath = NULL;
WCHAR msg[MAXPATH+MAXMSG] = {0};
IFileOpenDialog *ofd = NULL;
IShellItem *file = NULL;
BOOL isQueued = FALSE;
const COMDLG_FILTERSPEC fileSpec[] = {
{ L"CSV Text Files", L"*.csv" },
{ L"All Files", L"*.*" },
};
hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&ofd));
if (SUCCEEDED(hr)) {
ofd->SetTitle(L"Choose file");
ofd->SetFileTypes(ARRAYSIZE(fileSpec), fileSpec);
hr = ofd->Show(NULL);
if(SUCCEEDED(hr))
hr = ofd->GetResult(&file);
if(SUCCEEDED(hr)) {
hr = file->GetDisplayName(SIGDN_FILESYSPATH, &filePath);
if(SUCCEEDED(hr)){
if(PathExists(filePath)) {
StringCchPrintf(msg, ARRAYSIZE(msg), L"The file %s is already loaded.", filePath);
MessageBox(NULL, msg, L"appname", MB_OK);
}
else {
EnterCriticalSection(&m_MountQueueLock);
isQueued = !m_MountQueue.Find(filePath) ? TRUE : FALSE;
if(!isQueued)
m_MountQueue.Add(filePath);
LeaveCriticalSection(&m_MountQueueLock);
if(!isQueued) {
CCsv::BuildTree(filePath);
}
else {
StringCchPrintf(msg, ARRAYSIZE(msg), L"The file %s is already being loaded.", filePath);
MessageBox(NULL, msg, L"appname", MB_OK);
}
}
CoTaskMemFree(filePath);
}
file->Release();
}
ofd->Release();
}
return success;
}
int main() {
CoInitialize(NULL);
CMountPath m;
m.Mount();
CoUninitialize();
return 0;
}
your conceptual mistake here:
if(!isQueued)
{
m_MountQueue.Add(wstring(filePathHeap));
CreateThread(NULL, 0, CCsv::BuildTree, static_cast<LPVOID>(filePathHeap), 0, NULL);
}
so you insert filePathHeap in some database and simultaneously pass this filePathHeap to some thread. question - who is OWNER of filePathHeap ? who must free it ? if you free it from BuildTree - in m_MountQueue will be invalid pointer ? you not show code who and how handle m_MountQueue - may be this code pop and free filePathHeap before it used in BuildTree.
in general - if you push filePathHeap to m_MountQueue - you must not direct use it pointer after this, but have working thread (pool) which pop data from m_MountQueue , process and free it. or if you direct use filePathHeap you must not insert it to m_MountQueue.
in case if you need simultaneously working with filePathHeap from several threads - you need ref count have. some like this:
class CFilePath
{
PWSTR _filePath;
LONG _dwRef;
~CFilePath()
{
CoTaskMemFree(_filePath);
}
public:
PCWSTR getPath() { return _filePath; }
CFilePath(PWSTR filePath)
{
_filePath = filePath;
_dwRef = 1;
}
void AddRef()
{
InterlockedIncrement(&_dwRef);
}
void Release()
{
if (!InterlockedDecrement(&_dwRef)) delete this;
}
};
ULONG CALLBACK CCsv::BuildTree(CFilePath* p)
{
MessageBoxW(0, p->getPath(), L"use path in thread 2", MB_OK);
p->Release();
return 0;
}
BOOL CMountPath::Mount() {
...
if (CFilePath* p = new CFilePath(filePath))
{
p->AddRef();
if (HANDLE hThread = CreateThread(0, 0, reinterpret_cast<PTHREAD_START_ROUTINE>(CCsv::BuildTree), p, 0, 0))
{
CloseHandle(hThread);
}
else
{
p->Release();
}
MessageBoxW(0, p->getPath(), L"use path in thread 1", MB_OK);
p->Release();
}
else
{
CoTaskMemFree(filePath);
}
...
}
Secondly - this code
LPWSTR filePathHeap = (LPWSTR)CoTaskMemAlloc(MAXPATH * sizeof(WCHAR));
if(filePathHeap) StringCchCopy(filePathHeap, MAXPATH, filePath);
absolutely unnecessary. you can direct use filePath as is, but not reallocate it. for what ?!
thirdly - as in previous answer - almost no sense create thread and after this just wait on it exit.
HANDLE hThread = CreateThread(NULL, 0, CCsv::BuildTree, static_cast<LPVOID>(filePathHeap), 0, NULL);
// there is no wait in the namespace extension. the wait is just to keep the console app main thread running.
if(INVALID_HANDLE_VALUE != hThread)
WaitForSingleObject(hThread, INFINITE);
why in this case not direct call CCsv::BuildTree(filePathHeap); with same effect ?
also can note that INVALID_HANDLE_VALUE returned only for CreateFile or Socket - so for file handles only. all another object creating api (including CreateThread return 0 on error, but not INVALID_HANDLE_VALUE (-1) ) - so you incorrect check error condition.
and finally who will call CloseHandle(hThread) ? handle not auto closed when thread exit
Ey,
I'm making threading system in lua and I have a crash when I resume a lot threads...
I'm noob in C Lua and I don't really know what is the good or bad what I'm doing in the next codes...
I flagged crash places with "//maybe here"
#include<cstdio>
#include<ala_lua.h>
extern"C"{
void __stdcall Sleep(unsigned long);
};
int main(){
lua_State*L=luaL_newstate();
luaL_openlibs(L);
luaA_libs(L);
lua_State*T=lua_newthread(L);
luaL_loadfile(T,"c:/clua.lua");
co_resume(T);
do{
Sleep(1);
}while(co_update());
lua_close(L);
gets(new char[1]);
return 0;
};
#ifndef ALA_LUA_H
#define ALA_LUA_H
#include<ala_lua_co.h>
void luaA_libs(lua_State*);
static int luaA_sleep(lua_State*handle){
co_sleep(handle,lua_tointeger(handle,1));
return 0;
};
static int luaA_rthread(lua_State*handle){
if(lua_isthread(handle,1)){
co_resume(lua_tothread(handle,1));
};
return 0;
};
static int luaA_mthread(lua_State*handle){
if(lua_isfunction(handle,1)){
lua_State*thread=lua_newthread(handle);
lua_pushvalue(handle,1);
lua_xmove(handle,thread,1);
return 1;
};
return 0;
};
void luaA_libs(lua_State*handle){
lua_register(handle,"mthread",luaA_mthread);
lua_register(handle,"rthread",luaA_rthread);
lua_register(handle,"sleep",luaA_sleep);
};
#endif
#ifndef ALA_LUA_CO_H
#define ALA_LUA_CO_H
#include<vector>
#include<time.h>
#include<stdio.h>
#include<cstring>
#include<lua.hpp>
std::vector<lua_State*>co_threads;
std::vector<time_t*>co_threads_delay;
static const size_t CO_NPOS=-1;
bool co_update();
size_t co_new(lua_State*);
size_t co_get(lua_State*);
void co_resume(lua_State*);
void co_report(lua_State*);
void co_rem(lua_State*,size_t);
void co_sleep(lua_State*,time_t);
void co_remifdead(lua_State*,size_t);
void co_remifdead(lua_State*handle,size_t index=CO_NPOS){
switch(lua_status(handle)){
case LUA_OK:{
if(lua_gettop(handle)==0)co_rem(handle,index);
return;
};
case LUA_ERRRUN:{
co_rem(handle,index);
return;
};
};
};
void co_rem(lua_State*handle,size_t index=CO_NPOS){
if(index==CO_NPOS){
index=co_get(handle);
};
if(index!=CO_NPOS){
co_threads.erase(co_threads.begin()+index);
delete[]co_threads_delay[index];
co_threads_delay.erase(co_threads_delay.begin()+index);
};
};
bool co_update(){
if(co_threads.empty())return false;
size_t i=co_threads.size();
while(i>0){
--i;
lua_State*handle=co_threads[i];
if(lua_status(handle)==LUA_YIELD){
if(*co_threads_delay[i]<=clock()){
lua_resume(handle,NULL,0);//here maybe
co_remifdead(handle,i);
};
}else{
co_remifdead(handle,i);
};
};
return!co_threads.empty();
};
void co_resume(lua_State*handle){
switch(lua_status(handle)){
case LUA_YIELD:{
lua_resume(handle,NULL,0);
co_remifdead(handle);
return;
};
case LUA_OK:{
if(lua_gettop(handle)!=0){
size_t index=co_new(handle);
lua_resume(handle,NULL,0);//here maybe
co_remifdead(handle,index);
return;
}else{return;};
};
default:{return;};
};
};
void co_sleep(lua_State*handle,time_t slp){
size_t index=co_get(handle);
if(index!=CO_NPOS){
*co_threads_delay[index]=slp+clock();
lua_yield(co_threads[index],0);
};
};
void co_report(lua_State*handle){
if(lua_status(handle)==LUA_ERRRUN){
const char*error=lua_tostring(handle,-1);
#ifdef ALA_LUA_ERRLOG
FILE*file=fopen(ALA_LUA_ERRLOG,"a");
fputs(error,file);
fclose(file);
#else
puts(error);
#endif
lua_pop(handle,-1);
};
};
size_t co_get(lua_State*handle){
if(co_threads.empty())return CO_NPOS;
const size_t l=co_threads.size();
for(size_t i=0;i<l;++i){
if(co_threads[i]==handle)return i;
};
return CO_NPOS;
};
size_t co_new(lua_State*handle){
if(lua_status(handle)==LUA_OK&&lua_gettop(handle)!=0){
time_t*tm=new time_t[1];
co_threads.push_back(handle);
co_threads_delay.push_back(tm);
return co_threads.size()-1;
};
return CO_NPOS;
};
#endif
Lua has no built-in support for multithreading at the OS level. Lua threads are coroutines, not OS threads. You cannot use Lua threads of the same mother state in different OS threads. You can use separate Lua states in different OS threads but any data exchange between two states must be managed manually.
On the other hand, you can build Lua to support OS-level multithreading by defining lock and unlock macros but these will be called every time the app goes into and leaves Lua.