I am trying to debug sporadic access violations that occur inside a boost::interprocess message queue. (access violation reading an address in the shared memory region).
Environment: boost 1.54, VC++2010. Occurs in both Debug & Release builds.
It always occurs on or about line 854 (in case of reception) in message_queue.hpp:
Comments were added by me
recvd_size = top_msg.len; // top_msg points to invalid location
Or line 756 (in case of sending)
BOOST_ASSERT(free_msg_hdr.priority == 0); // free_msg_hdr points to invalid location
It appears as though this is related to the message queue creation. If a message queue is created "properly" (i.e. without the possible race condition), the error never occurs.
Otherwise it might occur on timed_receive() or timed_send() on the queue at seemingly random times.
I came up with a short example that represents the problem:
Unfortunately I cannot run it on Coliru, since it requires two processes.
One has to be started without any parameters, the second with any single parameter.
After a number of runs, one of the processes will crash in message_queue.
#include <iostream>
#include <boost/interprocess/ipc/message_queue.hpp>
#include <boost/thread.hpp>
#include <boost/assert.hpp>
#include <boost/date_time.hpp>
using namespace boost::interprocess;
using namespace boost::posix_time;
using boost::posix_time::microsec_clock; // microsec_clock is ambiguous between boost::posix_time and boost::interprocess. What are the odds?
int main(int argc, wchar_t** argv)
{
while(true)
{
int proc = 0;
message_queue* queues[2] = {NULL, NULL};
std::string names[] = {"msgq0", "msgq1"};
if(1 == argc)
{
proc = 0;
message_queue::remove(names[0].c_str());
if(NULL != queues[0]) { delete queues[0]; queues[0] = NULL; }
queues[0] = new message_queue(open_or_create, names[0].c_str(), 128, 10240);
bool bRet = false;
do
{
try
{
if(NULL != queues[1]) { delete queues[1]; queues[1] = NULL; }
queues[1]=new message_queue(open_only, names[1].c_str());
bRet = true;
}
catch(const interprocess_exception&)
{
//boost::this_thread::sleep(boost::posix_time::milliseconds(2));
delete queues[1];
queues[1] = NULL;
continue;
}
}while(!bRet);
}
else
{
proc = 1;
message_queue::remove(names[1].c_str());
if(NULL != queues[1]) { delete queues[1]; queues[1] = NULL; }
queues[1] = new message_queue(open_or_create, names[1].c_str(), 128, 10240);
bool bRet = false;
do
{
try
{
if(NULL != queues[0]) { delete queues[0]; queues[0] = NULL; }
queues[0]=new message_queue(open_only, names[0].c_str());
bRet = true;
}
catch(const interprocess_exception&)
{
//boost::this_thread::sleep(boost::posix_time::milliseconds(2));
delete queues[0];
queues[0] = NULL;
continue;
}
}while(!bRet);
}
long long nCnt = 0;
for(int i = 0; i < 1; ++i)
{
if(proc)
{
std::string sOut;
sOut = "Proc1 says: Hello ProcA " + std::to_string(nCnt) + " ";
sOut.resize(10230, ':');
for(int n = 0; n < 3; ++n)
{
queues[1]->timed_send(sOut.data(), sOut.size(), 0, ptime(boost::posix_time::microsec_clock::universal_time()) + milliseconds(1));
}
bool bMessage = false;
for(int n = 0; n < 3; ++n)
{
size_t nRec; unsigned int nPrio;
std::string sIn; sIn.resize(10240);
bMessage = queues[0]->timed_receive(&sIn[0], 10240, nRec, nPrio, ptime(boost::posix_time::microsec_clock::universal_time()) + milliseconds(1));
if(bMessage)
{
sIn.resize(nRec);
//std::cout << sIn << " ";
}
}
if(bMessage)
{
//std::cout << std::endl;
}
}
else
{
std::string sOut;
sOut = "Proc0 says: Hello Procccccccdadae4325a " + std::to_string(nCnt);
sOut.resize(10240, '.');
for(int n = 0; n < 3; ++n)
{
queues[0]->timed_send(sOut.data(), sOut.size(), 0, ptime(boost::posix_time::microsec_clock::universal_time()) + milliseconds(1));
}
bool bMessage = false;
for(int n = 0; n < 3; ++n)
{
size_t nRec; unsigned int nPrio;
std::string sIn; sIn.resize(10240);
bMessage = queues[1]->timed_receive(&sIn[0], 10240, nRec, nPrio, ptime(boost::posix_time::microsec_clock::universal_time()) + milliseconds(1));
if(bMessage)
{
sIn.resize(nRec);
//std::cout << sIn << " ";
}
}
if(bMessage)
{
//std::cout << std::endl;
}
}
nCnt++;
boost::this_thread::sleep(boost::posix_time::milliseconds(10));
}
}
return 0;
}
I am still thinking I might be doing something wrong, since I cannot find anything about this problem anywhere else, and the boost libraries are normally very good.
Is there anything I might be doing wrong with the usage of the message_queue in this example?
I don't think that both processes using open_or_create is a supported idiom. Are you aware of this thread on the mailing list? I can't find more discussions so it looks to me like lifetime management wasn't eventually considered necessary to add.
Thus you'll need to synchronise the creation manually with boost::interprocess or possibly by having one of the processes retrying to open_only the queue until the other process creates it.
Related
In a previous question, I asked how to implement asynchronous I/O. This code now works, except that at the end it never stops. It seems that aio_read reads starting at offset, for length, and if it is past the end of the file, the operation succeeds? This code builds and runs on Ubuntu 20.04LTS and successfully reads blocks 1-5, each 512 bytes, then when it runs out of file it keeps oscillating between block 4 and 5. It never terminates.
Here is the code:
#include <aio.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <condition_variable>
#include <cstring>
#include <iostream>
#include <thread>
using namespace std;
using namespace std::chrono_literals;
constexpr uint32_t blockSize = 512;
mutex readMutex;
bool readReady = false;
condition_variable cv;
bool operation_completed = false;
int fh;
int bytesRead;
void process(char* buf, uint32_t bytesRead) {
cout << "processing..." << endl;
usleep(100000);
}
void aio_completion_handler(sigval_t sigval) {
struct aiocb* req = (struct aiocb*)sigval.sival_ptr;
// check whether asynch operation is complete
int status;
if ((status = aio_error(req)) != 0) {
cout << "Error: " << status << '\n';
return;
}
int ret = aio_return(req);
bytesRead = req->aio_nbytes;
cout << "ret == " << ret << endl;
cout << (char*)req->aio_buf << endl;
unique_lock<mutex> readLock(readMutex);
operation_completed = true;
cv.notify_one();
}
void thready() {
char* buf1 = new char[blockSize];
char* buf2 = new char[blockSize];
aiocb cb;
char* processbuf = buf1;
char* readbuf = buf2;
fh = open("smallfile.dat", O_RDONLY);
if (fh < 0) {
throw std::runtime_error("cannot open file!");
}
memset(&cb, 0, sizeof(aiocb));
cb.aio_fildes = fh;
cb.aio_nbytes = blockSize;
cb.aio_offset = 0;
// Fill in callback information
/*
Using SIGEV_THREAD to request a thread callback function as a notification
method
*/
cb.aio_sigevent.sigev_notify_attributes = nullptr;
cb.aio_sigevent.sigev_notify = SIGEV_THREAD;
cb.aio_sigevent.sigev_notify_function = aio_completion_handler;
/*
The context to be transmitted is loaded into the handler (in this case, a
reference to the aiocb request itself). In this handler, we simply refer to
the arrived sigval pointer and use the AIO function to verify that the request
has been completed.
*/
cb.aio_sigevent.sigev_value.sival_ptr = &cb;
int cursor = 0;
int currentBytesRead = read(fh, buf1, blockSize); // read the 1st block
while (true) {
cb.aio_buf = readbuf;
operation_completed = false; // set predicate to true and wait until asynch changes it
cb.aio_offset = cursor;
aio_read(&cb); // each next block is read asynchronously
process(processbuf, currentBytesRead); // process while waiting
{
unique_lock<mutex> readLock(readMutex);
cv.wait( readLock, []{ return operation_completed; } );
}
if (!operation_completed)
break;
currentBytesRead = bytesRead; // make local copy of global modified by the asynch code
cursor += bytesRead;
if (currentBytesRead < blockSize) {
break; // last time, get out
}
cout << "back from wait" << endl;
swap(processbuf, readbuf); // switch to other buffer for next time
currentBytesRead = bytesRead; // create local copy
}
delete[] buf1;
delete[] buf2;
}
int main() {
try {
thready();
} catch (std::exception& e) {
cerr << e.what() << '\n';
}
return 0;
}
First, is the above code an appropriate way to do this to get the length of the file and figure out exactly how many reads to do?
Second, if this is so, fine, but how can aio_read just return success if I try to read past the end of file? Error status is always zero. I am confused about what it is supposed to do.
with 512 bytes of each of 1,2,3,4,5
I am using the Windows api Gatt Client BLE for C++, my goal is to connect two devices (but in this case I will try just one) and keep reading and writing data constantly without closing the device at any time. All my devices have one specific service that contains a read characteristic and a write one.
HOW TO TEST:
Use Visual studio 2017 (v141) with Windows SDK Version: 10.0.18362.0, create a new console (.exe) solution, change the Platform in Project -> Properties to Win32 and go to Project -> Properties -> C/C++ -> Command Line and add these options:
/std:c++17 /await
Then copy the following code in a file (you can copy all in the same .cpp file):
#pragma once
#include <SDKDDKVer.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <iostream>
#include <queue>
#include <map>
#include <mutex>
#include <condition_variable>
#include <string>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Web.Syndication.h>
#include "winrt/Windows.Devices.Bluetooth.h"
#include "winrt/Windows.Devices.Bluetooth.GenericAttributeProfile.h"
#include "winrt/Windows.Devices.Enumeration.h"
#include "winrt/Windows.Storage.Streams.h"
#pragma comment(lib, "windowsapp")
using namespace std;
using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::Web::Syndication;
using namespace Windows::Devices::Bluetooth;
using namespace Windows::Devices::Bluetooth::GenericAttributeProfile;
using namespace Windows::Devices::Enumeration;
using namespace Windows::Storage::Streams;
#pragma region STRUCS AND ENUMS
#define LOG_ERROR(e) cout << e << endl;
union to_guid
{
uint8_t buf[16];
guid guid;
};
const uint8_t BYTE_ORDER[] = { 3, 2, 1, 0, 5, 4, 7, 6, 8, 9, 10, 11, 12, 13, 14, 15 };
guid make_guid(const wchar_t* value)
{
to_guid to_guid;
memset(&to_guid, 0, sizeof(to_guid));
int offset = 0;
for (unsigned int i = 0; i < wcslen(value); i++) {
if (value[i] >= '0' && value[i] <= '9')
{
uint8_t digit = value[i] - '0';
to_guid.buf[BYTE_ORDER[offset / 2]] += offset % 2 == 0 ? digit << 4 : digit;
offset++;
}
else if (value[i] >= 'A' && value[i] <= 'F')
{
uint8_t digit = 10 + value[i] - 'A';
to_guid.buf[BYTE_ORDER[offset / 2]] += offset % 2 == 0 ? digit << 4 : digit;
offset++;
}
else if (value[i] >= 'a' && value[i] <= 'f')
{
uint8_t digit = 10 + value[i] - 'a';
to_guid.buf[BYTE_ORDER[offset / 2]] += offset % 2 == 0 ? digit << 4 : digit;
offset++;
}
else
{
// skip char
}
}
return to_guid.guid;
}
mutex subscribeLock;
condition_variable subscribeSignal;
mutex _mutexWrite;
condition_variable signalWrite;
struct DeviceCacheEntry {
BluetoothLEDevice device = nullptr;
GattDeviceService service = nullptr;
GattCharacteristic characteristic = nullptr;
};
map<wstring, DeviceCacheEntry> cache;
struct Subscription {
GattCharacteristic::ValueChanged_revoker revoker;
};
struct BLEDeviceData {
wstring id;
wstring name;
bool isConnectable = false;
Subscription* subscription = NULL;
};
vector<BLEDeviceData> deviceList{};
mutex deviceListLock;
condition_variable deviceListSignal;
#pragma endregion
#pragma region CACHE FUNCTIONS
//Call this function to get a device from cache or async if it wasn't found
IAsyncOperation<BluetoothLEDevice> getDevice(wchar_t* deviceId) {
if (cache.count(wstring(deviceId)) && cache[wstring(deviceId)].device)
co_return cache[wstring(deviceId)].device;
BluetoothLEDevice result = co_await BluetoothLEDevice::FromIdAsync(deviceId);
if (result == nullptr) {
LOG_ERROR("Failed to connect to device.")
co_return nullptr;
}
else {
DeviceCacheEntry d;
d.device = result;
if (!cache.count(wstring(deviceId))) {
cache.insert({ wstring(deviceId), d });
}
else {
cache[wstring(deviceId)] = d;
}
co_return cache[wstring(deviceId)].device;
}
}
//Call this function to get a service from cache or async if it wasn't found
IAsyncOperation<GattDeviceService> getService(wchar_t* deviceId, wchar_t* serviceId) {
if (cache.count(wstring(deviceId)) && cache[wstring(deviceId)].service)
co_return cache[wstring(deviceId)].service;
auto device = co_await getDevice(deviceId);
if (device == nullptr)
co_return nullptr;
GattDeviceServicesResult result = co_await device.GetGattServicesForUuidAsync(make_guid(serviceId), BluetoothCacheMode::Cached);
if (result.Status() != GattCommunicationStatus::Success) {
LOG_ERROR("Failed getting services. Status: " << (int)result.Status())
co_return nullptr;
}
else if (result.Services().Size() == 0) {
LOG_ERROR("No service found with uuid")
co_return nullptr;
}
else {
if (cache.count(wstring(deviceId))) {
cache[wstring(deviceId)].service = result.Services().GetAt(0);
}
co_return cache[wstring(deviceId)].service;
}
}
//Call this function to get a characteristic from cache or async if it wasn't found
IAsyncOperation<GattCharacteristic> getCharacteristic(wchar_t* deviceId, wchar_t* serviceId, wchar_t* characteristicId) {
try {
if (cache.count(wstring(deviceId)) && cache[wstring(deviceId)].characteristic)
co_return cache[wstring(deviceId)].characteristic;
auto service = co_await getService(deviceId, serviceId);
if (service == nullptr)
co_return nullptr;
GattCharacteristicsResult result = co_await service.GetCharacteristicsForUuidAsync(make_guid(characteristicId), BluetoothCacheMode::Cached);
if (result.Status() != GattCommunicationStatus::Success) {
LOG_ERROR("Error scanning characteristics from service. Status: " << (int)result.Status())
co_return nullptr;
}
else if (result.Characteristics().Size() == 0) {
LOG_ERROR("No characteristic found with uuid")
co_return nullptr;
}
else {
if (cache.count(wstring(deviceId))) {
cache[wstring(deviceId)].characteristic = result.Characteristics().GetAt(0);
}
co_return cache[wstring(deviceId)].characteristic;
}
}
catch (...) {
LOG_ERROR("Exception while trying to get characteristic")
}
}
#pragma endregion
#pragma region SCAN DEVICES FUNCTIONS
DeviceWatcher deviceWatcher{ nullptr };
mutex deviceWatcherLock;
DeviceWatcher::Added_revoker deviceWatcherAddedRevoker;
DeviceWatcher::Updated_revoker deviceWatcherUpdatedRevoker;
DeviceWatcher::Removed_revoker deviceWatcherRemovedRevoker;
DeviceWatcher::EnumerationCompleted_revoker deviceWatcherCompletedRevoker;
struct TestBLE {
static void ScanDevices();
static void StopDeviceScan();
};
//This function would be called when a new BLE device is detected
void DeviceWatcher_Added(DeviceWatcher sender, DeviceInformation deviceInfo) {
BLEDeviceData deviceData;
deviceData.id = wstring(deviceInfo.Id().c_str());
deviceData.name = wstring(deviceInfo.Name().c_str());
if (deviceInfo.Properties().HasKey(L"System.Devices.Aep.Bluetooth.Le.IsConnectable")) {
deviceData.isConnectable = unbox_value<bool>(deviceInfo.Properties().Lookup(L"System.Devices.Aep.Bluetooth.Le.IsConnectable"));
}
deviceList.push_back(deviceData);
}
//This function would be called when an existing BLE device is updated
void DeviceWatcher_Updated(DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate) {
wstring deviceData = wstring(deviceInfoUpdate.Id().c_str());
for (int i = 0; i < deviceList.size(); i++) {
if (deviceList[i].id == deviceData) {
if (deviceInfoUpdate.Properties().HasKey(L"System.Devices.Aep.Bluetooth.Le.IsConnectable")) {
deviceList[i].isConnectable = unbox_value<bool>(deviceInfoUpdate.Properties().Lookup(L"System.Devices.Aep.Bluetooth.Le.IsConnectable"));
}
break;
}
}
}
void DeviceWatcher_Removed(DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate) {
}
void DeviceWatcher_EnumerationCompleted(DeviceWatcher sender, IInspectable const&) {
TestBLE::StopDeviceScan();
TestBLE::ScanDevices();
}
//Call this function to scan async all BLE devices
void TestBLE::ScanDevices() {
try {
lock_guard lock(deviceWatcherLock);
IVector<hstring> requestedProperties = single_threaded_vector<hstring>({ L"System.Devices.Aep.DeviceAddress", L"System.Devices.Aep.IsConnected", L"System.Devices.Aep.Bluetooth.Le.IsConnectable" });
hstring aqsFilter = L"(System.Devices.Aep.ProtocolId:=\"{bb7bb05e-5972-42b5-94fc-76eaa7084d49}\")"; // list Bluetooth LE devices
deviceWatcher = DeviceInformation::CreateWatcher(aqsFilter, requestedProperties, DeviceInformationKind::AssociationEndpoint);
deviceWatcherAddedRevoker = deviceWatcher.Added(auto_revoke, &DeviceWatcher_Added);
deviceWatcherUpdatedRevoker = deviceWatcher.Updated(auto_revoke, &DeviceWatcher_Updated);
deviceWatcherRemovedRevoker = deviceWatcher.Removed(auto_revoke, &DeviceWatcher_Removed);
deviceWatcherCompletedRevoker = deviceWatcher.EnumerationCompleted(auto_revoke, &DeviceWatcher_EnumerationCompleted);
deviceWatcher.Start();
}
catch (exception e) {
LOG_ERROR(e.what())
}
}
void TestBLE::StopDeviceScan() {
scoped_lock lock(deviceListLock, deviceWatcherLock);
if (deviceWatcher != nullptr) {
deviceWatcherAddedRevoker.revoke();
deviceWatcherUpdatedRevoker.revoke();
deviceWatcherRemovedRevoker.revoke();
deviceWatcherCompletedRevoker.revoke();
deviceWatcher.Stop();
deviceWatcher = nullptr;
}
deviceListSignal.notify_one();
}
#pragma endregion
#pragma region SUBSCRIBE/READ FUNCTIONS
//On this function you can read all data from the specified characteristic
void Characteristic_ValueChanged(GattCharacteristic const& characteristic, GattValueChangedEventArgs args)
{
LOG_ERROR("Read data from device: " << to_string(characteristic.Service().Device().DeviceId()) << ", data size: " << args.CharacteristicValue().Length())
}
//Function used to subscribe async to the specific device
fire_and_forget SubscribeCharacteristicAsync(wstring deviceId, wstring serviceId, wstring characteristicId, bool* result) {
try {
auto characteristic = co_await getCharacteristic(&deviceId[0], &serviceId[0], &characteristicId[0]);
if (characteristic != nullptr) {
auto status = co_await characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue::Notify);
if (status != GattCommunicationStatus::Success) {
LOG_ERROR("Error subscribing to characteristic. Status: " << (int)status)
}
else {
for (int i = 0; i < deviceList.size(); i++) {
if (deviceList[i].id == deviceId) {
deviceList[i].subscription = new Subscription();
deviceList[i].subscription->revoker = characteristic.ValueChanged(auto_revoke, &Characteristic_ValueChanged);
break;
}
}
if (result != 0)
*result = true;
}
}
}
catch (hresult_error& ex)
{
LOG_ERROR("SubscribeCharacteristicAsync error: " << to_string(ex.message().c_str()))
for (int i = 0; i < deviceList.size(); i++) {
if (deviceList[i].id == deviceId && deviceList[i].subscription) {
delete deviceList[i].subscription;
deviceList[i].subscription = NULL;
break;
}
}
}
subscribeSignal.notify_one();
}
//Call this function to subscribe to the specific device so you can read data from it
bool SubscribeCharacteristic(wstring deviceId, wstring serviceId, wstring characteristicId) {
unique_lock<mutex> lock(subscribeLock);
bool result = false;
SubscribeCharacteristicAsync(deviceId, serviceId, characteristicId, &result);
subscribeSignal.wait(lock);
return result;
}
#pragma endregion
#pragma region WRITE FUNCTIONS
//Function used to send data async to the specific device
fire_and_forget SendDataAsync(wchar_t* deviceId, wchar_t* serviceId, wchar_t* characteristicId, uint8_t * data, uint16_t size, bool* result) {
try {
auto characteristic = co_await getCharacteristic(deviceId, serviceId, characteristicId);
if (characteristic != nullptr) {
DataWriter writer;
writer.WriteBytes(array_view<uint8_t const>(data, data + size));
IBuffer buffer = writer.DetachBuffer();
auto status = co_await characteristic.WriteValueAsync(buffer, GattWriteOption::WriteWithoutResponse);
if (status != GattCommunicationStatus::Success) {
LOG_ERROR("Error writing value to characteristic. Status: " << (int)status)
}
else if (result != 0) {
LOG_ERROR("Data written succesfully")
*result = true;
}
}
}
catch (hresult_error& ex)
{
LOG_ERROR("SendDataAsync error: " << to_string(ex.message().c_str()))
for (int i = 0; i < deviceList.size(); i++) {
if (deviceList[i].id == deviceId && deviceList[i].subscription) {
delete deviceList[i].subscription;
deviceList[i].subscription = NULL;
break;
}
}
}
signalWrite.notify_one();
}
//Call this function to write data on the device
bool SendData(wchar_t* deviceId, wchar_t* serviceId, wchar_t* characteristicId, uint8_t * data, uint16_t size) {
bool result = false;
unique_lock<mutex> lock(_mutexWrite);
// copy data to stack so that caller can free its memory in non-blocking mode
SendDataAsync(deviceId, serviceId, characteristicId, data, size, &result);
signalWrite.wait(lock);
return result;
}
#pragma endregion
Finally copy this main function (it can be copied at the end of the same file):
int main() {
//The mac of the device that will be tested
wstring deviceMac = L"00:11:22:33:44:55";
//These are the serviceUUID, readCharacteristicUUID and writeCharacteristicUUID as I said previously
wstring serviceUUID = L"{47918888-5555-2222-1111-000000000000}";
wstring readUUID = L"{31a28888-5555-2222-1111-00000000cede}";
wstring writeUUID = L"{f55a8888-5555-222-1111-00000000957a}";
//I think it is the mac of the BLE USB Dongle because it is in all device id when they are enumerated
wstring otherMac = L"24:4b:fe:3a:1a:ba";
//The device Id that we are looking for
wstring deviceId = L"BluetoothLE#BluetoothLE" + otherMac;
deviceId += L"-";
deviceId += deviceMac;
//To start scanning just call this function
TestBLE::ScanDevices();
//Data to be written all the time
const uint16_t dataSize = 3;
uint8_t data [dataSize]= { 0x0, 0xff, 0xff };
//Wait time in miliseconds between each write
chrono::milliseconds waitTime = 100ms;
//It will be executed always
while (true) {
//Then every device and their info updated would be in this vector
for (int i = 0; i < deviceList.size(); i++) {
//If the device is connectable we will try to connect if we aren't subscribed yet or send information
if (deviceList[i].isConnectable) {
//We can do here the following code to know the structure of the device id (if otherMac variable is the BLE USB dongle mac or not)
//cout << to_string(deviceList[i].id) << endl;
if (!deviceList[i].subscription && deviceList[i].id == deviceId) {
SubscribeCharacteristic(deviceList[i].id, serviceUUID, readUUID);
}
else if (deviceList[i].subscription) {
SendData(&deviceId[0], &serviceUUID[0], &writeUUID[0], data, dataSize);
}
}
}
this_thread::sleep_for(waitTime);
}
}
You will need a BLE device with a service that contains a reading and a writing characteristic, set the corresponding values in the deviceMac, serviceUUID, readUUID and writeUUID variables, you can also modify the bytes that are going to be written in data and dataSize, and the time between writes in waitTime. The otherMac variable should be the mac of the BLE USB dongle device but I recommend that you check it by getting the id of the devices from deviceList inside the for loop.
When you run this code on some rare times you will get the error "Failed getting services. Status:" with result 1 (unreachable) or 3 (access denied) and in the rest of the cases it will be reading the device data correctly and after a while it will give the error "SendDataAsync error: Object has been disposed" and from there it will continue giving "SubscribeCharacteristicAsync error: Object has been disposed", so at some point it will stop being able to read data of the device. What could be the reason?
EDIT 1:
It is quite strange because with this code the data is never written correctly (the "Data written succesfully" message is not displayed) but in my completed code I have always been able to write the data, maybe the problem is still the same and it is related to the characteristic stored in the "map <wstring, DeviceCacheEntry> cache" since perhaps it is stored as a copy and when trying to access it at some point it is disposed by Windows (since it is a copy of the original that is stored in the cache) and gives the error as described in the answer to this post in the point named "UPDATE 2 - SOME WEIRDNESS"
I'm using Windows Web Services to access a WCF service and it creates a an array of pointers of the results.
I had a similar issue where I was also reading garbage data and I though it might be caused by sendign this data to another function that was causing the pointers to get invalidated but now I'm storing it in a vector reference to try to avoid such issues but the problem persists. I'm not sure what do do anymore.
I also tried using ListaDeMaterialesBE** lstmat = new ListaDeMaterialesBE*; but that did not work at all.
Here is the code:
void svc_listMaterials(const size_t& idProject, std::vector<ListaDeMaterialesBE>& result) {
HRESULT hr = ERROR_SUCCESS;
WS_ERROR* error = NULL;
WS_HEAP* heap = NULL;
WS_SERVICE_PROXY* proxy = NULL;
WS_ENDPOINT_ADDRESS address = {};
const WS_STRING url = WS_STRING_VALUE(L"http://localhost/SIMSE_Service.ServicioEstadistico.svc");
address.url = url;
WS_HTTP_BINDING_TEMPLATE templateValue = {};
ULONG maxMessageSize = 2147483647;
WS_CHANNEL_PROPERTY channelProperty [ 1 ];
channelProperty [ 0 ].id = WS_CHANNEL_PROPERTY_MAX_BUFFERED_MESSAGE_SIZE;
channelProperty [ 0 ].value = &maxMessageSize;
channelProperty [ 0 ].valueSize = sizeof(maxMessageSize);
WS_CHANNEL_PROPERTIES channelProperties;
channelProperties.properties = channelProperty;
channelProperties.propertyCount = 1;
templateValue.channelProperties = channelProperties;
hr = WsCreateError(NULL, 0, &error);
if (FAILED(hr)) {
}
hr = WsCreateHeap(2048, 512, NULL, 0, &heap, error);
WS_HTTP_BINDING_TEMPLATE templ = {};
hr = BasicHttpBinding_IServicioEstadistico_CreateServiceProxy(&templateValue, NULL, 0, &proxy, error);
hr = WsOpenServiceProxy(proxy, &address, NULL, error);
// The issue starts here
ListaDeMaterialesBE** lstmat;
unsigned int rcount;
hr = BasicHttpBinding_IServicioEstadistico_ListarMaterialesPorProyecto(proxy,
idProject,
&rcount,
&lstmat,
heap,
NULL,
NULL,
NULL,
error);
// Here is where we seem to be reading garbage data
if (rcount > 1 && rcount < 10000) {
for (size_t i = 1; i <= rcount; ++i) {
ListaDeMaterialesBE t = **lstmat;
result.push_back(t);
lstmat++;
}
}
if (proxy) {
WsCloseServiceProxy(proxy, NULL, NULL);
WsFreeServiceProxy(proxy);
}
if (heap) {
WsFreeHeap(heap);
}
if (error) {
WsFreeError(error);
}
if (FAILED(hr)) {
std::cout << "errored \n";
}
}
I think the reason for the error is your wrong use of the returned secondary pointer.
I created a simple example to simulate the problem:
#define COUNT 10
void f(int** pp)
{
*pp = new int[COUNT];
for (int i = 0; i < COUNT; i++)
{
(*pp)[i] = i;
}
}
int main(int argc, const char* argv[])
{
int** pp = new int*;
f(pp);
for (int i = 0; i < COUNT; i++)
{
cout << **pp;
pp++;
}
return 0;
}
The function f obtains a secondary pointer, this secondary pointer saves the first address of an array, and then writes it.
Then use your method to traverse the array, it will cause an access exception:
The reason for the error is that when you use the secondary pointer to perform an auto-increment operation, the distance it moves each time is not the distance of an element you think.
So you should get the first address of the array it saves through the secondary pointer, and then traverse:
int** pp = new int*;
f(pp);
auto p = *pp;
for (int i = 0; i < COUNT; i++)
{
cout << *p;
p++;
}
Then we can read the correct data:
I'm new to pthread and attempting to implement a producer/consumer problem that will let the user pick buffer size, # of producers, # of consumers, and total # of items. I've been looking through what I thought were similar things on stack overflow, but can't seem to get it right.
My code has a main class and it spawns producers and consumers. It hands the producers and consumers a pointer to a stack initialized in main (or at least I'm trying to). I thought that what I was doing was legal and in CLion I get the predictive text options I want so I thought I linked it properly but I'm segfaulting when I try to read the top element.
I used GDB to make sure I knew what line I was crashing on, but don't understand what's wrong with my setup. While debugging I confirmed that a producer goes through its push() command first, but the consumer fails when attempting top() or pop(). I'd seen some threads here where the OP had problems because they didn't join their threads, but I am so I'm a little lost.
#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <stack>
#include <cstring>
#include <semaphore.h>
#include <pthread.h>
#define N 10000
sem_t mutex;
sem_t fullCount;
sem_t emptyCount;
int iCount = 0;
typedef struct _thread_data{
int id;
int itemcount;
std::stack<char>* ptr;
}thread_data;
void *producer(void *arg){
std::cout << "spawned producer\n";
thread_data *data = (thread_data *)arg;
while(true){
char message = 'X';
sem_wait(&emptyCount);
sem_wait(&mutex);
if(iCount < data->itemcount){
data->ptr->push(message);
iCount++;
char temp [25];
sprintf(temp, "p:<%u>, item: %c, at %d\n", data->id, message, data->ptr->size());
std::cout << temp;
//std::cout << "hi I'm a producer\n";
sem_post(&mutex);
sem_post(&fullCount);
}
else{
sem_post(&fullCount);
sem_post(&mutex);
pthread_exit(nullptr);
}
}
}
void *consumer(void *arg){
std::cout << ("spawned consumer\n");
thread_data *data = (thread_data *)arg;
while(true){
char message;
sem_wait(&fullCount);
sem_wait(&mutex);
if(iCount < data->itemcount) {
message = data->ptr->top(); //SEGFAULT
char temp[25];
printf(temp, "c:<%u>, item: %c, at %d\n", data->id, message, data->ptr->size());
data->ptr->pop();
std::cout << temp;
//std::cout << "Hi I'm a consumer\n";
sem_post(&mutex);
sem_post(&emptyCount);
}
else if (iCount == data->itemcount){
message = data->ptr->top(); //SEGFAULT
char temp[25];
printf(temp, "c:<%u>, item: %c, at %d\n", data->id, message, data->ptr->size());
data->ptr->pop();
std::cout << temp;
sem_post(&emptyCount);
sem_post(&mutex);
pthread_exit(nullptr);
}
else{
sem_post(&mutex);
pthread_exit(nullptr);
}
}
}
int main(int argc, char *argv[]){
int bufferSize = N;
int pThreadCount,cThreadCount,itemCount;
for (int x = 0; x < argc; ++x) {
if(strcmp(argv[x],"-b") == 0){
bufferSize = atoi(argv[x+1]);
}
if(strcmp(argv[x],"-p") == 0){
pThreadCount = atoi(argv[x+1]);
}
if(strcmp(argv[x],"-c") == 0){
cThreadCount = atoi(argv[x+1]);
}
if(strcmp(argv[x],"-i") == 0){
itemCount = atoi(argv[x+1]);
}
}
sem_init(&mutex,1,1);
sem_init(&fullCount,1,0);
sem_init(&emptyCount,1,bufferSize);
std::stack<char> myStack;
pthread_t myPThreads[pThreadCount];
thread_data thrData[pThreadCount];
pthread_t myCThreads[cThreadCount];
thread_data cThrData[cThreadCount];
for (int i = 0; i < pThreadCount; ++i) {
thrData[i].id = i;
thrData[i].itemcount = itemCount;
thrData[i].ptr = &myStack;
pthread_create(&myPThreads[i], NULL, producer, &thrData[i]);
}
for (int i = 0; i < cThreadCount; ++i) {
cThrData[i].id = i;
cThrData[i].itemcount = itemCount;
thrData[i].ptr = &myStack;
pthread_create(&myCThreads[i], NULL, consumer, &cThrData[i]);
}
for (int k = 0; k < pThreadCount; ++k) {
pthread_join(myPThreads[k], NULL);
}
for (int j = 0; j < cThreadCount; ++j) {
pthread_join(myCThreads[j], NULL);
}
return 0;
}
iCount <= data->itemcount is always true. consumer never exits the loop. At some point, it exhausts the stack, and the subsequent top() call exhibits undefined behavior.
cThrData[i].ptr is never initialized, for any i. consumer calls top() through an uninitialized pointer, whereupon the program exhibits undefined behavior.
Currently I'm using this function which I've cobbled together from reading several loosely related questions all over the internet. The problem I'm having is that the first time I ran it it returned an error, but unfortunately I haven't been able to reproduce it. Now when I run it it simply returns 0 every time.
DWORD GetAddressOfString(char *input)
{
unsigned char *p = NULL;
MEMORY_BASIC_INFORMATION info;
HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, _processID);
for (p = NULL; VirtualQueryEx(process, p, &info, sizeof(info)) == sizeof(info); p += info.RegionSize)
{
if (info.State == MEM_COMMIT && (info.Type == MEM_MAPPED || info.Type == MEM_PRIVATE))
{
char *buffer = new char[info.RegionSize];
SIZE_T bytesRead;
ReadProcessMemory(process, p, &buffer, info.RegionSize, &bytesRead);
for (int i = 0; i <= (info.RegionSize - sizeof(input)); i++)
{
if (memcmp(input, &buffer[i], sizeof(input)) == 0)
{
return i;
}
}
}
}
}
Here's a quick and dirty version that searches for data in itself. If you open up Notepad++, type "SomeDataToFind", replace the pid with the correct value, and run it, it should find the data as well. It might give you something to start with and embellish to suit your needs.
Your code was searching for the wrong length, returning the wrong offset, leaking memory like a sieve, and not always returning a value which is undefined behavior.
#include <Windows.h>
#include <iostream>
#include <string>
#include <vector>
char* GetAddressOfData(DWORD pid, const char *data, size_t len)
{
HANDLE process = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, pid);
if(process)
{
SYSTEM_INFO si;
GetSystemInfo(&si);
MEMORY_BASIC_INFORMATION info;
std::vector<char> chunk;
char* p = 0;
while(p < si.lpMaximumApplicationAddress)
{
if(VirtualQueryEx(process, p, &info, sizeof(info)) == sizeof(info))
{
p = (char*)info.BaseAddress;
chunk.resize(info.RegionSize);
SIZE_T bytesRead;
if(ReadProcessMemory(process, p, &chunk[0], info.RegionSize, &bytesRead))
{
for(size_t i = 0; i < (bytesRead - len); ++i)
{
if(memcmp(data, &chunk[i], len) == 0)
{
return (char*)p + i;
}
}
}
p += info.RegionSize;
}
}
}
return 0;
}
int main()
{
const char someData[] = "SomeDataToFind";
std::cout << "Local data address: " << (void*)someData << "\n";
//Pass whatever process id you like here instead.
DWORD pid = GetCurrentProcessId();
char* ret = GetAddressOfData(pid, someData, sizeof(someData));
if(ret)
{
std::cout << "Found: " << (void*)ret << "\n";
}
else
{
std::cout << "Not found\n";
}
return 0;
}