Is code a safe thread?

Discussion in 'C++' started by Nephi Immortal, Apr 17, 2011.

  1. I customize operator new to create my own definition.
    m_out_of_memory is a global variable of class Test. m_out_of_memory
    can be set to true or false inside operator new function before Test()
    is called to construct three data members: a, b, c.
    If m_out_of_memory is to be true, then Test() is not called and all
    data members cannot be initialized.
    My one question is – is thread safe? What happen if two main
    functions are running on two threads at the same time while accessing
    m_out_of_memory?
    First thread will report out of memory before second thread will skip
    to initialize data members. Can you please confirm that it never
    happened as long as thread safety is taken care?
    Or…is m_out_of_memory stored in separate memory on each thread and is
    never shared?
    I choose this method to avoid using exception.

    class Test
    {
    public:
    Test() : a( 1 ), b( 2 ), c( 3 ) {
    }

    ~Test() {
    }

    bool Is_Sufficient_Memory() const {
    return ( m_out_of_memory == false ) ? true : false;
    }

    void* operator new( size_t size ) {
    void* p = malloc( size );

    m_out_of_memory = ( p == 0 ) ? true : false;

    return p;
    }

    void operator delete( void* memory ) {
    free( memory );
    }

    void Set_abc( int _a, int _b, int _c ) {
    a = _a; b = _b; c = _c;
    }

    private:
    static bool m_out_of_memory;
    int a;
    int b;
    int c;
    };

    bool Test::m_out_of_memory = false;

    int main()
    {
    Test* T = new Test();

    if( T->Is_Sufficient_Memory()) {
    T->Set_abc( 10, 20, 30 );
    delete T;
    }

    return 0;
    }
    Nephi Immortal, Apr 17, 2011
    #1
    1. Advertising

  2. Nephi Immortal

    Kai-Uwe Bux Guest

    Nephi Immortal wrote:

    > I customize operator new to create my own definition.
    > m_out_of_memory is a global variable of class Test. m_out_of_memory
    > can be set to true or false inside operator new function before Test()
    > is called to construct three data members: a, b, c.
    > If m_out_of_memory is to be true, then Test() is not called and all
    > data members cannot be initialized.
    > My one question is ? is thread safe? What happen if two main
    > functions are running on two threads at the same time while accessing
    > m_out_of_memory?
    > First thread will report out of memory before second thread will skip
    > to initialize data members. Can you please confirm that it never
    > happened as long as thread safety is taken care?


    Well, it would be impossible to take care of thread safety with the
    interface design presented below. The content of the shared static variable
    is meant to indicate the status returned by the _previous_ allocation
    attempt. Hence, the meaning of the variable would differ from thread to
    thread (as they go through different allocation attempts and there is no
    trans-thread meaning of "last"). No synchronization would change that. The
    only way to deal with it, would be to stop any other threads when it tries
    to allocate memory until the first thread doing so has checked the status of
    _its_ last allocation attempt. Such synchronization is not possible with the
    interface you present.

    > Or?is m_out_of_memory stored in separate memory on each thread and is
    > never shared?


    Which data are shared depends on the particulars of the thread model. E.g.,
    in linux you could use multiple processes or multiple threads. In the later
    case, static data would be shared.

    > I choose this method to avoid using exception.


    Aha. And what's wrong with exceptions here?

    > class Test
    > {
    > public:
    > Test() : a( 1 ), b( 2 ), c( 3 ) {
    > }
    >
    > ~Test() {
    > }
    >
    > bool Is_Sufficient_Memory() const {
    > return ( m_out_of_memory == false ) ? true : false;
    > }
    >
    > void* operator new( size_t size ) {
    > void* p = malloc( size );
    >
    > m_out_of_memory = ( p == 0 ) ? true : false;
    >
    > return p;
    > }
    >
    > void operator delete( void* memory ) {
    > free( memory );
    > }
    >
    > void Set_abc( int _a, int _b, int _c ) {
    > a = _a; b = _b; c = _c;
    > }
    >
    > private:
    > static bool m_out_of_memory;
    > int a;
    > int b;
    > int c;
    > };
    >
    > bool Test::m_out_of_memory = false;
    >
    > int main()
    > {
    > Test* T = new Test();
    >
    > if( T->Is_Sufficient_Memory()) {


    Hm .....

    If the allocation via new() above fails, then T is 0 here and dereferencing
    the pointer is undefined behavior. And yes, accessing a member function
    qualifies as dereferencing even when the member function only accesses
    static data.

    > T->Set_abc( 10, 20, 30 );
    > delete T;
    > }
    >
    > return 0;
    > }


    One more question: why do not use a nothrow allocation function. In that
    case, a new expression would indicate an allocation failure not via an
    exception but by returning 0. You can test for that. No threading issues
    arise.


    Best,

    Kai-Uwe Bux
    Kai-Uwe Bux, Apr 17, 2011
    #2
    1. Advertising

  3. Nephi Immortal

    Kai-Uwe Bux Guest

    Kai-Uwe Bux wrote:

    > Nephi Immortal wrote:
    >
    >> I customize operator new to create my own definition.
    >> m_out_of_memory is a global variable of class Test. m_out_of_memory
    >> can be set to true or false inside operator new function before Test()
    >> is called to construct three data members: a, b, c.
    >> If m_out_of_memory is to be true, then Test() is not called and all
    >> data members cannot be initialized.
    >> My one question is ? is thread safe? What happen if two main
    >> functions are running on two threads at the same time while accessing
    >> m_out_of_memory?
    >> First thread will report out of memory before second thread will skip
    >> to initialize data members. Can you please confirm that it never
    >> happened as long as thread safety is taken care?

    >
    > Well, it would be impossible to take care of thread safety with the
    > interface design presented below. The content of the shared static
    > variable is meant to indicate the status returned by the _previous_
    > allocation attempt. Hence, the meaning of the variable would differ from
    > thread to thread (as they go through different allocation attempts and
    > there is no trans-thread meaning of "last"). No synchronization would
    > change that. The only way to deal with it, would be to stop any other
    > threads when it tries to allocate memory until the first thread doing so
    > has checked the status of _its_ last allocation attempt. Such
    > synchronization is not possible with the interface you present.


    Oops, maybe my imagination was too limited. I could think of something like
    this:

    class Test {
    ...

    void* operator new ( size_t size ) {
    lock the static variable m_out_of_memory;
    ...
    }

    static
    bool last_alloc_ok ( void ) {
    bool result = ...
    unlock the static variable m_out_of_memory
    return result;
    }

    ...

    };

    Note, however, that this intimately ties the observer function and the
    allocation function. You now must use them strictly alternating in each
    thread.

    Also, you can leave the locking issues to the calling threads.

    Neither solution seems to be appealing: they look much too error-prone.

    >> Or?is m_out_of_memory stored in separate memory on each thread and is
    >> never shared?

    >
    > Which data are shared depends on the particulars of the thread model.
    > E.g., in linux you could use multiple processes or multiple threads. In
    > the later case, static data would be shared.
    >
    >> I choose this method to avoid using exception.

    >
    > Aha. And what's wrong with exceptions here?
    >
    >> class Test
    >> {
    >> public:
    >> Test() : a( 1 ), b( 2 ), c( 3 ) {
    >> }
    >>
    >> ~Test() {
    >> }
    >>
    >> bool Is_Sufficient_Memory() const {
    >> return ( m_out_of_memory == false ) ? true : false;
    >> }
    >>
    >> void* operator new( size_t size ) {
    >> void* p = malloc( size );
    >>
    >> m_out_of_memory = ( p == 0 ) ? true : false;
    >>
    >> return p;
    >> }
    >>
    >> void operator delete( void* memory ) {
    >> free( memory );
    >> }
    >>
    >> void Set_abc( int _a, int _b, int _c ) {
    >> a = _a; b = _b; c = _c;
    >> }
    >>
    >> private:
    >> static bool m_out_of_memory;
    >> int a;
    >> int b;
    >> int c;
    >> };
    >>
    >> bool Test::m_out_of_memory = false;
    >>
    >> int main()
    >> {
    >> Test* T = new Test();
    >>
    >> if( T->Is_Sufficient_Memory()) {

    >
    > Hm .....
    >
    > If the allocation via new() above fails, then T is 0 here and
    > dereferencing the pointer is undefined behavior. And yes, accessing a
    > member function qualifies as dereferencing even when the member function
    > only accesses static data.
    >
    >> T->Set_abc( 10, 20, 30 );
    >> delete T;
    >> }
    >>
    >> return 0;
    >> }

    >
    > One more question: why do not use a nothrow allocation function. In that
    > case, a new expression would indicate an allocation failure not via an
    > exception but by returning 0. You can test for that. No threading issues
    > arise.
    >
    >
    > Best,
    >
    > Kai-Uwe Bux
    Kai-Uwe Bux, Apr 17, 2011
    #3
  4. On Apr 17, 1:05 pm, Kai-Uwe Bux <> wrote:
    > Nephi Immortal wrote:
    > > I customize operator new to create my own definition.
    > > m_out_of_memory is a global variable of class Test.  m_out_of_memory
    > > can be set to true or false inside operator new function before Test()
    > > is called to construct three data members: a, b, c.
    > > If m_out_of_memory is to be true, then Test() is not called and all
    > > data members cannot be initialized.
    > > My one question is ? is thread safe?  What happen if two main
    > > functions are running on two threads at the same time while accessing
    > > m_out_of_memory?
    > > First thread will report out of memory before second thread will skip
    > > to initialize data members.  Can you please confirm that it never
    > > happened as long as thread safety is taken care?

    >
    > Well, it would be impossible to take care of thread safety with the
    > interface design presented below. The content of the shared static variable
    > is meant to indicate the status returned by the _previous_ allocation
    > attempt. Hence, the meaning of the variable would differ from thread to
    > thread (as they go through different allocation attempts and there is no
    > trans-thread meaning of "last"). No synchronization would change that. The
    > only way to deal with it, would be to stop any other threads when it tries
    > to allocate memory until the first thread doing so has checked the statusof
    > _its_ last allocation attempt. Such synchronization is not possible with the
    > interface you present.
    >
    > > Or?is m_out_of_memory stored in separate memory on each thread and is
    > > never shared?

    >
    > Which data are shared depends on the particulars of the thread model. E.g..,
    > in linux you could use multiple processes or multiple threads. In the later
    > case, static data would be shared.
    >
    > > I choose this method to avoid using exception.

    >
    > Aha. And what's wrong with exceptions here?


    Well, I avoid exception for some reasons. Some programmers choose
    exception, but I choose alternative method. I prefer to use error
    message handler. Another reason, C++ Compiler is unable to inline
    some member functions in release mode if exception is enabled. And
    other reason, exception degrade performance.
    Please do not discuss the topic – exceptions. It is not important

    >
    >
    > > class Test
    > > {
    > > public:
    > > Test() : a( 1 ), b( 2 ), c( 3 ) {
    > > }

    >
    > > ~Test() {
    > > }

    >
    > > bool Is_Sufficient_Memory() const {
    > > return ( m_out_of_memory == false ) ? true : false;
    > > }

    >
    > > void* operator new( size_t size ) {
    > > void* p = malloc( size );

    >
    > > m_out_of_memory = ( p == 0 ) ? true : false;

    >
    > > return p;
    > > }

    >
    > > void operator delete( void* memory ) {
    > > free( memory );
    > > }

    >
    > > void Set_abc( int _a, int _b, int _c ) {
    > > a = _a; b = _b; c = _c;
    > > }

    >
    > > private:
    > > static bool m_out_of_memory;
    > > int a;
    > > int b;
    > > int c;
    > > };

    >
    > > bool Test::m_out_of_memory = false;

    >
    > > int main()
    > > {
    > > Test* T = new Test();

    >
    > > if( T->Is_Sufficient_Memory()) {

    >
    > Hm .....
    >
    > If the allocation via new() above fails, then  T is 0 here and dereferencing
    > the pointer is undefined behavior. And yes, accessing a member function
    > qualifies as dereferencing even when the member function only accesses
    > static data.


    Of course, you are correct. ‘this’ pointer is undefined if T is
    zero. Accessing static data member is not a problem. I wonder “if( T-
    >Is_Sufficient_Memory())” is undefined. I should declare static on

    Is_Sufficient_Memory() to become global function.

    >
    > > T->Set_abc( 10, 20, 30 );
    > > delete T;
    > > }

    >
    > > return 0;
    > > }

    >
    > One more question: why do not use a nothrow allocation function. In that
    > case, a new expression would indicate an allocation failure not via an
    > exception but by returning 0. You can test for that. No threading issues
    > arise.


    I can test T to be zero if memory allocation failed.
    Is_Sufficient_Memory() is preferable choice over “if( T == NULL )”
    because of better readable code.
    Calling operator new and operator delete hundred times can slow the
    memory alloction. Invoke operator new to allocate huge array over 10
    megabytes one time and then invoke operator delete to free it prior to
    the end of main() function.
    A huge array is declared static. Each class has its own non-static
    data member as memory address pointer. Each time, class constructs
    memory address pointer to point to the shared huge array. This allows
    many classes to share one array. The data can be erased before
    another class can reuse the same memory space. My method runs faster
    to eliminate the need to use malloc() and free() frequently.
    My code is carefully designed NOT to overrun buffers outside memory
    address pointer and the buffer length.
    You got the point right that this code is not concerned with thread
    safety.


    .......


    >
    > Best,
    >
    > Kai-Uwe Bux- Hide quoted text -
    >
    > - Show quoted text -
    Nephi Immortal, Apr 18, 2011
    #4
  5. Nephi Immortal

    Kai-Uwe Bux Guest

    Nephi Immortal wrote:

    > On Apr 17, 1:05 pm, Kai-Uwe Bux <> wrote:
    >> Nephi Immortal wrote:
    >> > I customize operator new to create my own definition.
    >> > m_out_of_memory is a global variable of class Test. m_out_of_memory
    >> > can be set to true or false inside operator new function before Test()
    >> > is called to construct three data members: a, b, c.
    >> > If m_out_of_memory is to be true, then Test() is not called and all
    >> > data members cannot be initialized.
    >> > My one question is ? is thread safe? What happen if two main
    >> > functions are running on two threads at the same time while accessing
    >> > m_out_of_memory?
    >> > First thread will report out of memory before second thread will skip
    >> > to initialize data members. Can you please confirm that it never
    >> > happened as long as thread safety is taken care?

    >>
    >> Well, it would be impossible to take care of thread safety with the
    >> interface design presented below. The content of the shared static
    >> variable is meant to indicate the status returned by the _previous_
    >> allocation attempt. Hence, the meaning of the variable would differ from
    >> thread to thread (as they go through different allocation attempts and
    >> there is no trans-thread meaning of "last"). No synchronization would
    >> change that. The only way to deal with it, would be to stop any other
    >> threads when it tries to allocate memory until the first thread doing so
    >> has checked the status of _its_ last allocation attempt. Such
    >> synchronization is not possible with the interface you present.
    >>
    >> > Or?is m_out_of_memory stored in separate memory on each thread and is
    >> > never shared?

    >>
    >> Which data are shared depends on the particulars of the thread model.
    >> E.g., in linux you could use multiple processes or multiple threads. In
    >> the later case, static data would be shared.
    >>
    >> > I choose this method to avoid using exception.

    >>
    >> Aha. And what's wrong with exceptions here?

    >
    > Well, I avoid exception for some reasons. Some programmers choose
    > exception, but I choose alternative method. I prefer to use error
    > message handler. Another reason, C++ Compiler is unable to inline
    > some member functions in release mode if exception is enabled. And
    > other reason, exception degrade performance.
    > Please do not discuss the topic ? exceptions. It is not important
    >
    >>
    >>
    >> > class Test
    >> > {
    >> > public:
    >> > Test() : a( 1 ), b( 2 ), c( 3 ) {
    >> > }

    >>
    >> > ~Test() {
    >> > }

    >>
    >> > bool Is_Sufficient_Memory() const {
    >> > return ( m_out_of_memory == false ) ? true : false;
    >> > }

    >>
    >> > void* operator new( size_t size ) {
    >> > void* p = malloc( size );

    >>
    >> > m_out_of_memory = ( p == 0 ) ? true : false;

    >>
    >> > return p;
    >> > }

    >>
    >> > void operator delete( void* memory ) {
    >> > free( memory );
    >> > }

    >>
    >> > void Set_abc( int _a, int _b, int _c ) {
    >> > a = _a; b = _b; c = _c;
    >> > }

    >>
    >> > private:
    >> > static bool m_out_of_memory;
    >> > int a;
    >> > int b;
    >> > int c;
    >> > };

    >>
    >> > bool Test::m_out_of_memory = false;

    >>
    >> > int main()
    >> > {
    >> > Test* T = new Test();

    >>
    >> > if( T->Is_Sufficient_Memory()) {

    >>
    >> Hm .....
    >>
    >> If the allocation via new() above fails, then T is 0 here and
    >> dereferencing the pointer is undefined behavior. And yes, accessing a
    >> member function qualifies as dereferencing even when the member function
    >> only accesses static data.

    >
    > Of course, you are correct. ?this? pointer is undefined if T is
    > zero. Accessing static data member is not a problem. I wonder ?if( T-
    >>Is_Sufficient_Memory())? is undefined. I should declare static on

    > Is_Sufficient_Memory() to become global function.
    >
    >>
    >> > T->Set_abc( 10, 20, 30 );
    >> > delete T;
    >> > }

    >>
    >> > return 0;
    >> > }

    >>
    >> One more question: why do not use a nothrow allocation function. In that
    >> case, a new expression would indicate an allocation failure not via an
    >> exception but by returning 0. You can test for that. No threading issues
    >> arise.

    >
    > I can test T to be zero if memory allocation failed.
    > Is_Sufficient_Memory() is preferable choice over ?if( T == NULL )?
    > because of better readable code.


    That is debatable. In particular, if you declare Is_Sufficient_Memory() as a
    non-member function. In that case it would be

    if ( T ) {
    ...
    }

    versus

    if ( Test::Is_Sufficient_Memory() ) {
    ...
    }

    The second version makes it very explicit, what the test is about. However,
    it suppresses any reference to the allocation whose success is being tested.

    You could do:

    static
    bool Was_Successfully_Allocated ( Test* T ) {
    return ( T != 0 );
    }

    and then use

    if ( Test::Was_Successfully_Allocated( T ) ) {
    ...
    }

    I don't feel that readability of code is improved by making that dependency
    implicit and hiding it behind a static variable that refers back to the last
    allocation. The last allocation need not have happened in nearby code (at
    least, there could be nothing but coding style to enforce it).

    > Calling operator new and operator delete hundred times can slow the
    > memory alloction. Invoke operator new to allocate huge array over 10
    > megabytes one time and then invoke operator delete to free it prior to
    > the end of main() function.
    > A huge array is declared static. Each class has its own non-static
    > data member as memory address pointer. Each time, class constructs
    > memory address pointer to point to the shared huge array. This allows
    > many classes to share one array. The data can be erased before
    > another class can reuse the same memory space. My method runs faster
    > to eliminate the need to use malloc() and free() frequently.
    > My code is carefully designed NOT to overrun buffers outside memory
    > address pointer and the buffer length.


    Sounds like you are implementing some kind of pool allocator.

    > You got the point right that this code is not concerned with thread
    > safety.


    Yes. And you got the point right that using a static variable will add
    complexity once thread safety becomes an issue to be dealt with.


    Best,

    Kai-Uwe Bux
    Kai-Uwe Bux, Apr 18, 2011
    #5
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Gabriel Rossetti
    Replies:
    0
    Views:
    1,294
    Gabriel Rossetti
    Aug 29, 2008
  2. Replies:
    1
    Views:
    326
    Brian Candler
    Aug 12, 2003
  3. Aredridel

    Not just $SAFE, but damn $SAFE

    Aredridel, Sep 2, 2004, in forum: Ruby
    Replies:
    19
    Views:
    226
  4. Farrel Lifson

    $SAFE =4 safe enough?

    Farrel Lifson, Aug 29, 2006, in forum: Ruby
    Replies:
    7
    Views:
    95
    Eric Hodel
    Aug 31, 2006
  5. John Nagle
    Replies:
    5
    Views:
    451
    John Nagle
    Mar 12, 2012
Loading...

Share This Page