четверг, 30 июня 2011 г.

Сериализация доступа к объекту

этот блог построен по мотивам одного топика,
просто, захотелось его продолжить, интересно же :)) ...

заведем базовый класс:
typedef struct tagBaseSynchPtr{
    CRITICAL_SECTION cs; 
    //  простой вход в критическую секцию
    VOID Enter( VOID ){
        ::EnterCriticalSection( &cs );
    }
    //  простой выход из критической секции
    VOID Leave( VOID ){
        ::LeaveCriticalSection( &cs );
    }
    tagBaseSynchPtr( VOID ){
        ::InitializeCriticalSection( &cs );
    }
    ~tagBaseSynchPtr( VOID ){
        ::DeleteCriticalSection( &cs );
    }
private:
    //  запрет на копирование, чтобы даже соблазна не возникало
    tagBaseSynchPtr&operator = ( const tagBaseSynchPtr&Src ){
        UNREFERENCED_PARAMETER( Src );
        return*this;
    }
}BASESYNCHPTR,*PBASESYNCHPTR;
теперь опишем на его основе синхронизатор "по ссылке":
template< typename Object >
class SynchPtr : public tagBaseSynchPtr{
protected:
    Object*    pObject;
public:
    //  прокси - объект-ссылка, собственно и реализующий сериализацию
    class Proxy{
        protected:
            SynchPtr< Object >* pPtr;
        public:
            Proxy( SynchPtr< Object >*pPtr = NULL ) : pPtr( pPtr ){
                if( pPtr ){
                    pPtr->Enter();
                }
            }
            //  добавлен копирующий конструктор
            Proxy( const typename SynchPtr< Object >::Proxy&Src ):
                pPtr( Src.pPtr ){
                    if( pPtr ){
                        pPtr->Enter();
                    }
                }
            //  конструкторы и деструктор не случайно используют NULL,
            //  таким образом, можно принудительно выйти
            //  из критической секции в любой момент, если нужно
            ~Proxy( VOID ){
                if( pPtr ){
                    pPtr->Leave();
                }
            }
            Object*operator -> ( VOID ){
                return pPtr->pObject;
            }
            //  добавлен константный доступ
            const Object*operator -> ( VOID )const{
                return pPtr->pObject;
            }
            //  добавлен оператор присваивания, т.е. прокси-указатели
            //  можно спокойно использовать по старинке: обрамляя
            //  синхронизаторами блоки кода, который невозможно
            //  или затруднительно представить атомарным вызовом
            Proxy&operator = ( const Proxy&Src ){
                if( pPtr ){
                    pPtr->Leave();
                }
                pPtr    = Src.pPtr;
                if( pPtr ){
                    pPtr->Enter();
                }
                return*this;
            }
    };
    typename SynchPtr< Object >::Proxy operator->( VOID ){
        return this;
    }
    SynchPtr< Object >( Object*pObject ) : pObject( pObject ){}
    ~SynchPtr< Object >( VOID ){}
    SynchPtr< Object >&operator = ( Object*pObject ){
        this->pObject = pObject;
        return*this;
    }
private:
    //  аналогичный запрет на копирование
    SynchPtr< Object >&operator = ( const SynchPtr< Object >&Src ){
        UNREFERENCED_PARAMETER( Src );
        return*this;
    }
};
теперь опишем синхронизатор "относительно базы", такой SafePtr требуется, когда проксируемый объект может в моменты синхронизации менять свой базовый адрес, например: из параллельного потока
template< typename Object, typename Owner >
class RelSynchPtr : public tagBaseSynchPtr{
protected:
    //  RelSynchPtr объект требует 2 поля (см. далее)
    Owner    RelSynchPtr< Object, Owner >::*pOwner;
    Object   Owner::*pObject;
public:
    class Proxy{
        protected:
            RelSynchPtr< Object, Owner >* pPtr;
        public:
            Proxy( RelSynchPtr< Object, Owner >*pPtr = NULL ):
                pPtr( pPtr ){
                    if( pPtr ){
                        pPtr->Enter();
                    }
                }
            Proxy( const Proxy&Src ) : pPtr( Src.pPtr ){
                if( pPtr ){
                    pPtr->Enter();
                }
            }
            ~Proxy( VOID ){
                if( pPtr ){
                    pPtr->Leave();
                }
            }
            operator Object*( VOID ){
                return &(( pPtr->*( pPtr->pOwner )).*( pPtr->pObject ));
            }
            Object*operator -> ( VOID ){
                return &(( pPtr->*( pPtr->pOwner )).*( pPtr->pObject ));
            }
            const Object*operator -> ( VOID )const{
                return &(( pPtr->*( pPtr->pOwner )).*( pPtr->pObject ));
            }
            Proxy&operator = ( const Proxy&Src ){
                if( pPtr ){
                    pPtr->Leave();
                }
                pPtr    = Src.pPtr;
                if( pPtr ){
                    pPtr->Enter();
                }
                return*this;
            }
    };
    typename RelSynchPtr< Object, Owner >::Proxy operator->( VOID ){
        return this;
    }
    //  для инициализации такого объекта требуется 2 поля:
    //    - поле RelSynchPtr
    //    - поле самого объекта
    //  оба поля должны находиться в пределах одного класса Owner,
    //  в таком случае экземпляр Owner может менять базу в любой момент
    RelSynchPtr< Object, Owner >(
        RelSynchPtr< Object, Owner > Owner::*pThis,
        Object Owner::*pObject
    ) : pOwner( ::reverse_field( pThis ) ), pObject( pObject ){}
    ~RelSynchPtr< Object, Owner >( VOID ){}
};
сразу хочу предупредить, что RelSynchPtr реально будет дееспособным только в том случае, если CRITICAL_SECTION в tagBaseSynchPtr будет задан указателем, иначе при смене базы периодически будут возникать проблемы на выходе из критической секции

есть возможность на этой же основе реализовать и дроссель, но это немного позже...
у меня он уже реализован, но реальных испытаний пока для него небыло.

вспомогательные функции:
template< typename Data, typename Root >
inline Data Root::*__cdecl long_to_field( LONG offset ){
    union{
        LONG offset;
        Data Root::*field;
    }retval = { offset };
    return retval.field;
}
template< typename Data, typename Root >
inline LONG field_to_long( Data Root::*field ){
    union{
        Data Root::*field;
        LONG offset;
    }retval = { field };
    return retval.offset;
}
template< typename Data, typename Root >
inline Root Data::*reverse_field( Data Root::*field ){
    return long_to_field< Root, Data >(
        - field_to_long( field )
    );
}

Комментариев нет:

Отправить комментарий