00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #ifndef _OBBY_JUPITER_ALGORITHM_HPP_
00020 #define _OBBY_JUPITER_ALGORITHM_HPP_
00021
00022 #include <net6/non_copyable.hpp>
00023 #include "jupiter_error.hpp"
00024 #include "operation.hpp"
00025 #include "record.hpp"
00026
00027 namespace obby
00028 {
00029
00032 template<typename Document>
00033 class jupiter_algorithm: private net6::non_copyable
00034 {
00035 public:
00036 typedef Document document_type;
00037 typedef operation<document_type> operation_type;
00038 typedef record<document_type> record_type;
00039
00040 jupiter_algorithm();
00041 ~jupiter_algorithm();
00042
00045 std::auto_ptr<record_type> local_op(const operation_type& op);
00046
00050 std::auto_ptr<operation_type> remote_op(const record_type& rec);
00051 protected:
00055 class operation_storage: private net6::non_copyable
00056 {
00057 public:
00060 operation_storage(unsigned int count,
00061 const operation_type& op);
00062
00065 operation_storage(unsigned int count,
00066 std::auto_ptr<operation_type> op);
00067
00070 unsigned int get_count() const;
00071
00074 const operation_type& get_operation() const;
00075
00078 void reset_operation(const operation_type& new_op);
00079
00082 void reset_operation(std::auto_ptr<operation_type> new_op);
00083 protected:
00084 unsigned int m_count;
00085 std::auto_ptr<operation_type> m_operation;
00086 };
00087
00090 void discard_operations(const record_type& rec);
00091
00095 std::auto_ptr<operation_type> transform(const operation_type& op) const;
00096
00099 void check_preconditions(const record_type& rec) const;
00100
00101 protected:
00102 typedef std::list<operation_storage*> ack_list_type;
00103
00104 vector_time m_time;
00105 ack_list_type m_ack_list;
00106 };
00107
00108 template<typename Document>
00109 jupiter_algorithm<Document>::operation_storage::
00110 operation_storage(unsigned int count,
00111 const operation_type& op):
00112 m_count(count), m_operation(op.clone() )
00113 {
00114 }
00115
00116 template<typename Document>
00117 jupiter_algorithm<Document>::operation_storage::
00118 operation_storage(unsigned int count,
00119 std::auto_ptr<operation_type> op):
00120 m_count(count), m_operation(op)
00121 {
00122 }
00123
00124 template<typename Document>
00125 unsigned int jupiter_algorithm<Document>::operation_storage::get_count() const
00126 {
00127 return m_count;
00128 }
00129
00130 template<typename Document>
00131 const typename jupiter_algorithm<Document>::operation_type&
00132 jupiter_algorithm<Document>::operation_storage::get_operation() const
00133 {
00134 return *m_operation;
00135 }
00136
00137 template<typename Document>
00138 void jupiter_algorithm<Document>::operation_storage::
00139 reset_operation(const operation_type& new_op)
00140 {
00141 m_operation.reset(new_op.clone() );
00142 }
00143
00144 template<typename Document>
00145 void jupiter_algorithm<Document>::operation_storage::
00146 reset_operation(std::auto_ptr<operation_type> new_op)
00147 {
00148 m_operation = new_op;
00149 }
00150
00151 template<typename Document>
00152 jupiter_algorithm<Document>::jupiter_algorithm():
00153 m_time(0, 0)
00154 {
00155 }
00156
00157 template<typename Document>
00158 jupiter_algorithm<Document>::~jupiter_algorithm()
00159 {
00160 for(typename ack_list_type::iterator iter = m_ack_list.begin();
00161 iter != m_ack_list.end();
00162 ++ iter)
00163 {
00164 delete *iter;
00165 }
00166 }
00167
00168 template<typename Document>
00169 std::auto_ptr<typename jupiter_algorithm<Document>::record_type>
00170 jupiter_algorithm<Document>::local_op(const operation_type& op)
00171 {
00172 std::auto_ptr<record_type> rec(new record_type(m_time, op) );
00173 m_ack_list.push_back(new operation_storage(m_time.get_local(), op) );
00174 m_time.inc_local();
00175 return rec;
00176 }
00177
00178 template<typename Document>
00179 std::auto_ptr<typename jupiter_algorithm<Document>::operation_type>
00180 jupiter_algorithm<Document>::remote_op(const record_type& rec)
00181 {
00182 check_preconditions(rec);
00183 discard_operations(rec);
00184 std::auto_ptr<operation_type> op(transform(rec.get_operation()) );
00185 m_time.inc_remote();
00186 return op;
00187 }
00188
00189 template<typename Document>
00190 void jupiter_algorithm<Document>::discard_operations(const record_type& rec)
00191 {
00192 typename ack_list_type::iterator iter;
00193 for(iter = m_ack_list.begin(); iter != m_ack_list.end(); ++ iter)
00194 {
00195 if( (*iter)->get_count() < rec.get_time().get_remote() )
00196 delete *iter;
00197 else
00198 break;
00199 }
00200
00201 m_ack_list.erase(m_ack_list.begin(), iter);
00202
00203
00204
00205 if(rec.get_time().get_local() != m_time.get_remote() )
00206 {
00207 throw jupiter_error(
00208 "Sequence order mismatch: Incoming record's local "
00209 "time does not match own remote time"
00210 );
00211 }
00212 }
00213
00214 template<typename Document>
00215 std::auto_ptr<typename jupiter_algorithm<Document>::operation_type>
00216 jupiter_algorithm<Document>::transform(const operation_type& op) const
00217 {
00218 std::auto_ptr<operation_type> new_op(op.clone() );
00219
00220 for(typename ack_list_type::const_iterator iter = m_ack_list.begin();
00221 iter != m_ack_list.end();
00222 ++ iter)
00223 {
00224 const operation_type* existing_op =
00225 &(*iter)->get_operation();
00226 operation_type* new_trans_op =
00227 existing_op->transform(*new_op);
00228 operation_type* existing_trans_op =
00229 new_op->transform(*existing_op);
00230
00231 (*iter)->reset_operation(
00232 std::auto_ptr<operation_type>(existing_trans_op)
00233 );
00234
00235 new_op.reset(new_trans_op);
00236 }
00237
00238 return new_op;
00239 }
00240
00241 template<typename Document>
00242 void obby::jupiter_algorithm<Document>::
00243 check_preconditions(const record_type& rec) const
00244 {
00245 if(!m_ack_list.empty() &&
00246 rec.get_time().get_remote() < m_ack_list.front()->get_count() )
00247 {
00248 throw jupiter_error(
00249 "Transformation precondition failed: Incoming remote "
00250 "time is lower than oldest time in ack list"
00251 );
00252 }
00253
00254 if(rec.get_time().get_remote() > m_time.get_local() )
00255 {
00256 throw jupiter_error(
00257 "Transformation precondition failed: Incoming remote "
00258 "time is greater than own local time"
00259 );
00260 }
00261
00262 if(rec.get_time().get_local() != m_time.get_remote() )
00263 {
00264 throw jupiter_error(
00265 "Transformation precondition failed: Incoming local "
00266 "time does not match own remote time"
00267 );
00268 }
00269 }
00270
00271 }
00272
00273 #endif // _OBBY_JUPITER_ALGORITHM_HPP_