v8: How to ensure that weak callback runs at exit? - v8

I am having some trouble making the following code work. It is a slight extension of the material found at https://github.com/v8/v8/wiki/Embedders-Guide#accessing-dynamic-variables. It differs in that it has a constructor for the object, in addition to just the object. The code will run successfully, however will leak the native |PointAndPersistent| allocated in |PointConstructor|. I have attempted to provide a callback to clean it up with |SetWeak|, based on some code I found in d8 (https://cs.chromium.org/chromium/src/v8/src/d8.cc?q=DataAndPersistent&sq=package:chromium&l=215), however it is never executed. Does anyone know what the proper way to accomplish this is?
#include <cstdio>
#include "v8/include/libplatform/libplatform.h"
#include "v8/include/v8.h"
using namespace v8;
struct PointAndPersistent {
int x;
int y;
Global<Object> wrapper;
};
void PointAndPersistentCallback(
const WeakCallbackInfo<PointAndPersistent>& data) {
data.GetParameter()->wrapper.Reset();
delete data.GetParameter();
}
void PointConstructor(const FunctionCallbackInfo<Value>& args) {
PointAndPersistent* point_and_persistent = new PointAndPersistent();
if (args.Length() > 0) {
point_and_persistent->x = args[0]->Int32Value();
}
if (args.Length() > 1) {
point_and_persistent->y = args[1]->Int32Value();
}
point_and_persistent->wrapper.Reset(args.GetIsolate(), args.This());
point_and_persistent->wrapper.SetWeak(point_and_persistent,
PointAndPersistentCallback,
v8::WeakCallbackType::kParameter);
point_and_persistent->wrapper.MarkIndependent();
args.This()->SetInternalField(0,
External::New(args.GetIsolate(), point_and_persistent));
args.GetReturnValue().Set(args.This());
}
void GetPointX(Local<String> property,
const PropertyCallbackInfo<Value>& info) {
Local<Object> self = info.Holder();
Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));
void* ptr = wrap->Value();
int value = static_cast<PointAndPersistent*>(ptr)->x;
info.GetReturnValue().Set(value);
}
void SetPointX(Local<String> property, Local<Value> value,
const PropertyCallbackInfo<void>& info) {
Local<Object> self = info.Holder();
Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));
void* ptr = wrap->Value();
static_cast<PointAndPersistent*>(ptr)->x = value->Int32Value();
}
void GetPointY(Local<String> property,
const PropertyCallbackInfo<Value>& info) {
Local<Object> self = info.Holder();
Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));
void* ptr = wrap->Value();
int value = static_cast<PointAndPersistent*>(ptr)->y;
info.GetReturnValue().Set(value);
}
void SetPointY(Local<String> property, Local<Value> value,
const PropertyCallbackInfo<void>& info) {
Local<Object> self = info.Holder();
Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));
void* ptr = wrap->Value();
static_cast<PointAndPersistent*>(ptr)->y = value->Int32Value();
}
int main() {
Platform* platform = platform::CreateDefaultPlatform();
V8::InitializePlatform(platform);
V8::Initialize();
Isolate::CreateParams params;
params.array_buffer_allocator = ArrayBuffer::Allocator::NewDefaultAllocator();
Isolate* isolate = Isolate::New(params);
{
Isolate::Scope isolate_scope(isolate);
HandleScope handle_scope(isolate);
Local<FunctionTemplate> point_constructor_template =
FunctionTemplate::New(isolate, PointConstructor);
point_constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
point_constructor_template->InstanceTemplate()->SetAccessor(
String::NewFromUtf8(isolate, "x"), GetPointX, SetPointX);
point_constructor_template->InstanceTemplate()->SetAccessor(
String::NewFromUtf8(isolate, "y"), GetPointY, SetPointY);
Local<ObjectTemplate> point_template =
ObjectTemplate::New(isolate, point_constructor_template);
Local<ObjectTemplate> global_template = ObjectTemplate::New(isolate);
Local<String> name =
String::NewFromUtf8(isolate, "Point", NewStringType::kNormal)
.ToLocalChecked();
global_template->Set(name, point_constructor_template);
Local<Context> context = Context::New(isolate, nullptr, global_template);
Context::Scope context_scope(context);
Local<String> source = String::NewFromUtf8(isolate, "new Point(1, 2).x;",
NewStringType::kNormal)
.ToLocalChecked();
Local<Script> script = Script::Compile(context, source).ToLocalChecked();
Local<Value> result = script->Run(context).ToLocalChecked();
String::Utf8Value utf8(isolate, result);
printf("%s\n", *utf8);
}
isolate->Dispose();
V8::Dispose();
V8::ShutdownPlatform();
delete platform;
delete params.array_buffer_allocator;
}
Thanks!

Weak callbacks are invoked when the garbage collector determines that the object can be freed. A short-running program like your example has no need to ever run the garbage collector.
You can try to force a GC cycle before shutting down your app. Of course that will make your shutdown slower.
For the record, the documentation of SetWeak says:
NOTE: There is no guarantee as to when or even if the callback is
invoked. The invocation is performed solely on a best effort basis.
As always, GC-based finalization should not be relied upon for any
critical form of resource management!

Related

C++ Pointers and Destructors

class Dummy
{
public:
int* A{};
int num{};
public:
Dummy(int num)
{
this->num = num;
A = new int[num];
}
~Dummy()
{
delete[] A;
}
};
Dummy* dummy()
{
Dummy* d = new Dummy{ 4 };
d->A[0] = 1;
d->A[1] = 2;
d->A[2] = 3;
d->A[3] = 4;
return d;
}
int main()
{
Dummy* ATT = dummy();
}
When I tired to run this program This is always showing Expection at destructor and program can't continue further. What's wrong in this Code...
Your code uses new() and delete() which nowadays in C++ should mostly never be used. Please make yourself comfortable with the appropriate pointer classes which handle memory management for you, like shared_ptr or unique_ptr.

Force Move semantics

I'm trying to use move semantics (just as an experiment).
Here is my code:
class MyClass {
public:
MyClass(size_t c): count(c) {
data = new int[count];
}
MyClass( MyClass&& src) : count(src.count) {
data = src.data;
src.count = 0;
src.data = nullptr;
}
void operator=( MyClass&& src) {
data = src.data;
count = src.count;
src.count = 0;
src.data = nullptr;
}
~MyClass() {
if (data != nullptr)
delete[] data;
}
int* get_data() const {
return data;
}
size_t get_count() const {
return count;
}
private:
MyClass(const MyClass& src) : count(src.count) {
data = new int[src.count];
memcpy(data, src.data, sizeof(int)*src.count);
}
void operator=(const MyClass& src) {
count = src.count;
data = new int[src.count];
memcpy(data, src.data, sizeof(int)*src.count);
}
int* data;
size_t count;
};
int main()
{
MyClass mc(150);
for (size_t i = 0; i < mc.get_count(); ++i)
mc.get_data()[i] = i;
MyClass &&mc2 = std::move(mc);
return 0;
}
But std::move does not move mc to mc2, it just copies (copyies pointer as it is). If I remove copy constructor compiler generates it for MyClass.
How can I force move semantics to be used? How can I make it to be used in such constructions:
MyClass mc2(mc); //Move, not copy
-or-
MyClass mc2 = mc; //Move, not copy
I tried to use a '&&' operator to explicitely mark rvalue, but, of cause, it didn't work.
You're declaring m2 as a reference, not as a value. So it still refers to what it was initialised with, namely m1. You wanted this:
MyClass mc2 = std::move(mc);
Live example
As for the second part - there is no way to force a construct like these:
MyClass mc2(mc); //Move, not copy
//-or-
MyClass mc2 = mc; //Move, not copy
to move. If you want to move from an lvalue (and mc is indeed an lvalue), you have to use std::move (or another cast to rvalue) explicitly.
There is one thing you could do, but it would be a dirty hack, make the code unintuitive and be a great source for bugs. You could add an overload of the copy constructor (and copy assignment operator) taking a non-const reference, which would do the move. Basically something like std::auto_ptr used to do before it was rightfully deprecated. But it would never pass code review with me, for example. If you want to move, just std::move.
A few side notes:
Calling delete or delete[] on a null pointer is guaranteed to be a no-op, so you can safely drop the if from your destructor.
It's generally preferable to use std::copy instead of memcpy in C++ code, you don't have to worry about getting the sizeof right
You can force move semantics, if you delete the copy constructor and the assignment operator
MyClass(const MyClass& src)= delete;
void operator=(const MyClass& src) = delete;
in this case the provided move constructor or move assignment operator will be picked.
Rewrite your class a bit with some comments. Look over it, you might notice a few things you missed. Like:
in MyClass(size_t c) not checking for c != 0.
in void operator=(const MyClass& src) not delete[] data; (if exists) before reallocating.
And some other tiny details.Hope your compiler can handle this.
class MyClass {
private:
// initialize memebers directly
int* data = nullptr;
size_t count = 0;
public:
// default empty contructor
MyClass() = default;
// destructor
~MyClass() {
*this = nullptr; // use operator = (nullptr_t)
}
// allow nullptr construct
MyClass(nullptr_t):MyClass() {}
// allow nullptr assignment (for clearing)
MyClass& operator = (nullptr_t) {
if(data) {
delete[] data;
data = nullptr;
}
count = 0;
return *this;
}
// chain to default constructor, redundant in this case
MyClass(size_t c):MyClass() {
// maybe size_t is 0?
if(count = c) {
data = new int[count];
}
}
// chain to default constructor, redundant in this case
MyClass(MyClass&& src):MyClass() {
*this = std::move(src); // forward to move assignment
}
MyClass& operator=(MyClass&& src) {
// don't swap with self
if(&src != this) {
// it's better to swap and let src destroy when it feels like it.
// I always write move contructor and assignment to swap data.
// it's gonna be destroyed anyway, or not...
std::swap(src.data, data);
std::swap(src.count, count);
}
return *this;
}
MyClass(const MyClass& src):MyClass() {
*this = src; // forward to copy assignment
}
MyClass& operator = (const MyClass& src) {
// don't copy to self
if(&src != this) {
// delete first
if(data) {
delete[] data;
data = nullptr;
}
// now reallocate
if(count = src.count) {
data = new int[count];
memcpy(data, src.data, sizeof(int)* count);
}
}
return *this;
}
// easy way to use the object in a if(object) to test if it has content
explicit operator bool() const {
return data && count;
}
// same as above but made for if(!object) to test if empty
bool operator !() const {
return !data || !count;
}
public:
int* get_data() const {
return data;
}
size_t get_count() const {
return count;
}
// add more custom methods
};
Now to move you do this:
MyClass object1; // default construct
MyClass object1(5); // construct with capacity
MyClass object2(object1); // copy constructor
MyClass object3(std::move(object1)); // move constructor
object2 = object1; // copy assignment
object3 = std::move(object1); // move constructor
std::swap(object2, object3); // swap the two
object2 = nullptr; // to empty it
if(object1); // bool cast

Storing std::promise objects in a std::pair

I'm working on a couple of C++11 work queue classes. The first class, command_queue is a multi producer single consumer work queue. Multiple threads can post commands, and a single thread calls "wait()" and "pop_back()" in a loop to process those commands.
The second class, Actor uses command_queue and actually provides a consumer thread... besides that, the idea is that post() will return a future so that clients can either block until the command is processed, or continue running (actor also adds the idea of a result type). To implement this, I'm attempting to store std::promise's in a std::pair in the work queue. I believe I am fairly close, but i'm having a problem in the _entry_point function below... specifically, when I'm trying to get the std::pair out of the command queue I'm getting a "use of deleted function" compiler error... I'll put the actual error i'm getting from the compiler below the code (you should be able to save this to a text file and compile it yourself, it's stand alone c++11 code).
#include <mutex>
#include <condition_variable>
#include <future>
#include <list>
#include <stdio.h>
template<class T>
class command_queue
{
public:
command_queue() = default;
command_queue( const command_queue& ) = delete;
virtual ~command_queue() noexcept = default;
command_queue& operator = ( const command_queue& ) = delete;
void start()
{
std::unique_lock<std::recursive_mutex> g( _queueLock );
_started = true;
}
bool started()
{
return _started;
}
void stop()
{
std::unique_lock<std::recursive_mutex> g( _queueLock );
_started = false;
_queueCond.notify_one();
}
void post_front( const T& cmd )
{
std::unique_lock<std::recursive_mutex> g( _queueLock );
_queue.push_front( cmd );
_queueCond.notify_one();
}
void post_front( T&& cmd )
{
std::unique_lock<std::recursive_mutex> g( _queueLock );
_queue.push_front( cmd );
_queueCond.notify_one();
}
void wait()
{
std::unique_lock<std::recursive_mutex> g( _queueLock );
_queueCond.wait( g, [this](){return !this->_queue.empty() ? true : !this->_started;});
}
T pop_back()
{
std::unique_lock<std::recursive_mutex> g( _queueLock );
auto val = _queue.back();
_queue.pop_back();
return val;
}
private:
std::recursive_mutex _queueLock;
std::condition_variable_any _queueCond;
std::list<T> _queue;
bool _started = false;
};
template<class T, class U>
class actor
{
public:
actor() :
_started( false ),
_thread(),
_queue()
{
}
actor( const actor& ) = delete;
virtual ~actor() noexcept
{
if( _started )
stop();
}
actor& operator = ( const actor& ) = delete;
void start()
{
_started = true;
_queue.start();
_thread = std::thread( &actor<T,U>::_entry_point, this );
}
void stop()
{
_started = false;
_queue.stop();
_thread.join();
}
std::future<U> post( const T& cmd )
{
std::promise<U> p;
std::future<U> waiter = p.get_future();
_queue.post_front( std::pair<T,std::promise<U>>(cmd, std::move(p)) );
return waiter;
}
virtual U process( const T& cmd ) = 0;
protected:
void _entry_point()
{
while( _started )
{
_queue.wait();
if( !_started )
continue;
std::pair<T,std::promise<U>> item = _queue.pop_back();
item.second.set_value( process( item.first ) );
}
}
bool _started;
std::thread _thread;
command_queue<std::pair<T,std::promise<U>>> _queue;
};
class int_printer : public actor<int,bool>
{
public:
virtual bool process( const int& cmd ) override
{
printf("%d",cmd);
return true;
}
};
using namespace std;
int main( int argc, char* argv[] )
{
// std::promise<bool> p;
// list<std::pair<int,std::promise<bool>>> promises;
// promises.push_back( make_pair<int,std::promise<bool>>(10,std::move(p)) );
int_printer a;
a.start();
future<bool> result = a.post( 10 );
a.stop();
}
[developer#0800275b874e projects]$ g++ -std=c++11 pf.cpp -opf -lpthread
pf.cpp: In instantiation of ‘T command_queue<T>::pop_back() [with T = std::pair<int, std::promise<bool> >]’:
pf.cpp:133:65: required from ‘void actor<T, U>::_entry_point() [with T = int; U = bool]’
pf.cpp:99:9: required from ‘void actor<T, U>::start() [with T = int; U = bool]’
pf.cpp:163:13: required from here
pf.cpp:60:32: error: use of deleted function ‘constexpr std::pair<_T1, _T2>::pair(const std::pair<_T1, _T2>&) [with _T1 = int; _T2 = std::promise<bool>]’
In file included from /usr/lib/gcc/x86_64-redhat-linux/4.7.0/../../../../include/c++/4.7.0/utility:72:0,
from /usr/lib/gcc/x86_64-redhat-linux/4.7.0/../../../../include/c++/4.7.0/tuple:38,
from /usr/lib/gcc/x86_64-redhat-linux/4.7.0/../../../../include/c++/4.7.0/mutex:39,
from pf.cpp:2:
/usr/lib/gcc/x86_64-redhat-linux/4.7.0/../../../../include/c++/4.7.0/bits/stl_pair.h:119:17: note: ‘constexpr std::pair<_T1, _T2>::pair(const std::pair<_T1, _T2>&) [with _T1 = int; _T2 = std::promise<bool>]’ is implicitly deleted because the default definition would be ill-formed:
/usr/lib/gcc/x86_64-redhat-linux/4.7.0/../../../../include/c++/4.7.0/bits/stl_pair.h:119:17: error: use of deleted function ‘std::promise<_Res>::promise(const std::promise<_Res>&) [with _Res = bool]’
In file included from pf.cpp:4:0:
/usr/lib/gcc/x86_64-redhat-linux/4.7.0/../../../../include/c++/4.7.0/future:963:7: error: declared here
In file included from /usr/lib/gcc/x86_64-redhat-linux/4.7.0/../../../../include/c++/4.7.0/list:64:0,
from pf.cpp:5:
Promises aren't copyable (which makes sense - they represent a unique state). You need to use std::move in several places to transfer the unique ownership of the promise along.
Specifically, your home-brew queue class needs to permit moving, e.g.
auto val = std::move(_queue.back());
_queue.pop_back();
return val;
You protect writes to command_queue::_started with _queueLock, but not the read in command_queue::started(); this is a data race if some thread can call started while another thread is performing a modification (e.g., stop()).
Several small observations:
It doesn't make your program incorrect, but it's better to notify a condition variable outside the mutex. If you notify with the mutex held, another core may waste a microsecond or two scheduling a waiting thread to run only to immediately block on the mutex.
Your post_front(T&&) is copying the passed item into the queue due to missing a std::move:
_queue.push_front( cmd );
must be
_queue.push_front( std::move( cmd ) );
if you want it to actually be moved into the queue.
The predicate for the condition variable wait could be simplified from
[this](){return !this->_queue.empty() ? true : !this->_started;}
to
[this]{return !_queue.empty() || !_started;}
None of the command_queue member functions call other command_queue functions, so you could use a plain std::mutex instead of std::recursive_mutex and std::condition_variable instead of std::condition_variable_any.
You could use std::lock_guard<std::mutex> instead of std::unique_lock<std::mutex> to lock the mutex in every member function except wait. It's ever-so-slightly lighter weight.
You have the traditional pop exception-safety issue: If the selected move/copy constructor for T fails with an exception when returning from pop_back after modifying the queue, that element is lost. The way you've written the function makes this occurrence extremely unlikely, since
auto val = _queue.back();
_queue.pop_back();
return val;
(or after Kerrek's fix)
auto val = std::move(_queue.back());
_queue.pop_back();
return val;
should qualify for copy elision with a decent compiler, constructing the returned object in-place before the pop_back happens. Just be aware that if future changes impede copy elision you'll introduce the exception safety problem. You can avoid the issue altogether by passing a T& or optional<T>& as a parameter and move assigning the result to that parameter.
actor::_started is unnecessary since it's effectively a proxy for actor::_queue::_started.

Is there a way to create an elegant class-member window-function?

The Window-Procedure in the Win32 API must be static \ global function since it cannot take a class-object (the this) parameter. One can of-course use workarounds like a hWnd->object dictionary and such.
I wonder if D has a way to elegantly solve it, like create a tiny member function copy for each object (to call the object's real handler) or anonymous function that I can assign to WNDCLASS.lpfnWndProc (I know there are anonymous functions, but I cannot use the extern(Windows) property on them) ?
Can I do something like this :
class Window {
extern (Windows)
LRESULT delegate (HWND hWnd, UINT msg, WPARAM w, LPARAM l) MyWinProcDelegate;
this() {
MyWinProcDelegate = &Events;
}
extern (Windows)
LRESULT Events (HWND hWnd, UINT msg, WPARAM w, LPARAM l) {
MessageBoxA(null , "Success!!!" , null ,0);
return DefWindowProcA(hWnd, message, wParam, lParam);
}
}
(Omitting the registration\creation\msg-loop...)
The Events() doesn't seem to fire... am I missing something ?
Here I made this for you (based on BCS' answer):
version (Windows)
{
import std.c.windows.windows;
void makeExecutable(ubyte[] code)
{
DWORD old;
VirtualProtect(code.ptr, code.length, PAGE_EXECUTE_READWRITE, &old);
}
}
else
version (linux)
{
import core.sys.posix.sys.mman;
import core.sys.posix.unistd;
static if (!is(typeof(&mprotect)))
extern(C) int mprotect(void*, size_t, int);
void makeExecutable(ubyte[] code)
{
auto pageSize = sysconf(_SC_PAGE_SIZE);
auto address = ((cast(size_t)code.ptr) & ~(pageSize-1));
int pageCount =
(address/pageSize == (address+code.length)/pageSize) ? 1 : 2;
mprotect(cast(void*)address, pageSize * pageCount,
PROT_READ | PROT_WRITE | PROT_EXEC);
}
}
else
static assert(0, "TODO");
R function(A) delegate2function(R, A...)(R delegate(A) d)
{
enum size_t TEMPLATE1 = cast(size_t)0x01234567_01234567;
enum size_t TEMPLATE2 = cast(size_t)0x89ABCDEF_89ABCDEF;
static R functionTemplate(A args)
{
R delegate(A) d;
d.ptr = cast(typeof(d.ptr ))TEMPLATE1;
d.funcptr = cast(typeof(d.funcptr))TEMPLATE2;
return d(args);
}
static void functionTemplateEnd() {}
static void replaceWord(ubyte[] a, size_t from, size_t to)
{
foreach (i; 0..a.length - size_t.sizeof + 1)
{
auto p = cast(size_t*)(a.ptr + i);
if (*p == from)
{
*p = to;
return;
}
}
assert(0);
}
auto templateStart = cast(ubyte*)&functionTemplate;
auto templateEnd = cast(ubyte*)&functionTemplateEnd;
auto templateBytes = templateStart[0 .. templateEnd - templateStart];
// must allocate type with pointers, otherwise GC won't scan it
auto functionWords = new void*[(templateBytes.length / (void*).sizeof) + 3];
// store context in word-aligned boundary, so the GC can find it
functionWords[0] = d.ptr;
functionWords[1] = d.funcptr;
functionWords = functionWords[2..$];
auto functionBytes = (cast(ubyte[])functionWords)[0..templateBytes.length];
functionBytes[] = templateBytes[];
replaceWord(functionBytes, TEMPLATE1, cast(size_t)d.ptr );
replaceWord(functionBytes, TEMPLATE2, cast(size_t)d.funcptr);
makeExecutable(functionBytes);
return cast(typeof(return)) functionBytes.ptr;
}
void main()
{
import std.stdio;
auto context = 42;
void del(string s)
{
writeln(s);
writeln(context);
}
auto f = delegate2function(&del);
f("I am a pretty function");
}
Tested on Windows 32-bit and Linux 64-bit.
How about storing this in the window itself, with SetWindowLong?
One very un-portable solution would be to dynamically create a function that wraps the call. I would do this by writing a function that looks like this:
extern(C) RetType TestFn(Arg arg /* and any others */) {
Class c = cast(Class)(0xDEAD_BEEF);
return c.Method(arg);
}
You can then compile this function as un-optimized PIC, de-compile it, and find a byte sequence that can be mashed into what you need. The end result would be a type (likely a struct) that has a methoud returning a function pointer and that, when constructed, populates an internal void array with the bytes you found from the above step and pokes the object in question into the appropriate places.
A slightly more advanced solution would populate a delegate with both the object and the method pointer so both can be provided to the constructor. An even more advanced solution would template the type and take advantage of knowledge of the C and D calling conventions to dynamically generate the argument forwarding code.

Sharing an object between threads

How would you set the object data that is shared between threads and needs to be updated once after the complete cycle of (say) two threads in busy loop?
CRITICAL_SECTION critical_section_;
int value; //needs to be updated once after the cycle of any number of threads running in busy loop
void ThreadsFunction(int i)
{
while (true)
{
EnterCriticalSection(&critical_section_);
/* Lines of Code */
LeaveCriticalSection(&critical_section_);
}
}
Edit: The value can be an object of any class.
Two suggestions:
Make the object itself thread safe.
Pass the object into the thread as instance data
I'll use C++ as a reference in my example. You can easily transpose this to pure C if you want.
// MyObject is the core data you want to share between threads
struct MyObject
{
int value;
int othervalue;
// all all the other members you want here
};
class MyThreadSafeObject
{
private:
CRITICAL_SECTION _cs;
MyObject _myojbect;
bool _fLocked;
public:
MyThreadSafeObject()
{
_fLocked = false
InitializeCriticalSection();
}
~MYThreadSafeObject()
{
DeleteCriticalSection();
}
// add "getter and setter" methods for each member in MyObject
int SetValue(int x)
{
EnterCriticalSection(&_cs);
_myobject.value = x;
LeaveCriticalSection(&_cs);
}
int GetValue()
{
int x;
EnterCriticalSection(&_cs);
x = _myobject.value;
LeaveCriticalSection(&_cs);
return x;
}
// add "getter and setter" methods for each member in MyObject
int SetOtherValue(int x)
{
EnterCriticalSection(&_cs);
_myobject.othervalue = x;
LeaveCriticalSection(&_cs);
}
int GetOtherValue()
{
int x;
EnterCriticalSection(&_cs);
x = _myobject.othervalue;
LeaveCriticalSection(&_cs);
return x;
}
// and if you need to access the whole object directly without using a critsec lock on each variable access, add lock/unlock methods
bool Lock(MyObject** ppObject)
{
EnterCriticalSection(&_cs);
*ppObject = &_myobject;
_fLocked = true;
return true;
}
bool UnLock()
{
if (_fLocked == false)
return false;
_fLocked = false;
LeaveCriticalSection();
return true;
}
};
Then, create your object and thread as follows:
MyThreadSafeObject* pObjectThreadSafe;
MyObject* pObject = NULL;
// now initilaize your object
pObjectThreadSafe->Lock(&pObject);
pObject->value = 0; // initailze value and all the other members of pObject to what you want them to be.
pObject->othervalue = 0;
pObjectThreadSafe->Unlock();
pObject = NULL;
// Create your threads, passing the pointer to MyThreadSafeObject as your instance data
DWORD dwThreadID = 0;
HANDLE hThread = CreateThread(NULL, NULL, ThreadRoutine, pObjectThreadSafe, 0, &dwThreadID);
And your thread will operate as follows
DWORD __stdcall ThreadFunction(void* pData)
{
MyThreadSafeObject* pObjectThreadSafe = (MyThreadSafeObject*)pData;
MyObject* pObject = NULL;
while (true)
{
/* lines of code */
pObjectThreadSafe->SetValue(x);
/* lines of code */
}
}
If you want implement thread safe update of an integer you should better use InterlockedIncrement and InterlockedDecrement or InterlockedExchangeAdd functions. See http://msdn.microsoft.com/en-us/library/ms684122(VS.85).aspx.
If you do need use EnterCriticalSection and LeaveCriticalSection you will find an example in http://msdn.microsoft.com/en-us/library/ms686908(v=VS.85).aspx, but I recommend you to use EnterCriticalSection inside of __try block and LeaveCriticalSection inside of the __finally part of this blocks.

Resources