1 ///////////////////////////////////////////////////////////////////////////////
\r
3 // File : $Id: events.cpp,v 1.1 2007/05/05 17:00:42 faust Exp $
\r
4 // Subject : IBPP, internal EventsImpl class implementation
\r
6 ///////////////////////////////////////////////////////////////////////////////
\r
8 // (C) Copyright 2000-2006 T.I.P. Group S.A. and the IBPP Team (www.ibpp.org)
\r
10 // The contents of this file are subject to the IBPP License (the "License");
\r
11 // you may not use this file except in compliance with the License. You may
\r
12 // obtain a copy of the License at http://www.ibpp.org or in the 'license.txt'
\r
13 // file which must have been distributed along with this file.
\r
15 // This software, distributed under the License, is distributed on an "AS IS"
\r
16 // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
\r
17 // License for the specific language governing rights and limitations
\r
18 // under the License.
\r
20 ///////////////////////////////////////////////////////////////////////////////
\r
23 // * Tabulations should be set every four characters when editing this file.
\r
25 // SPECIAL WARNING COMMENT (by Olivier Mascia, 2000 Nov 12)
\r
26 // The way this source file handles events is not publicly documented, in
\r
27 // the ibase.h header file or in the IB 6.0 documentation. This documentation
\r
28 // suggests to use the API isc_event_block to construct vectors of events.
\r
29 // Unfortunately, this API takes a variable number of parameters to specify
\r
30 // the list of event names. In addition, the documentation warn on not using
\r
31 // more than 15 names. This is a sad limitation, partly because the maximum
\r
32 // number of parameters safely processed in such an API is very compiler
\r
33 // dependant and also because isc_event_counts() is designed to return counts
\r
34 // through the IB status vector which is a vector of 20 32-bits integers !
\r
35 // From reverse engineering of the isc_event_block() API in
\r
36 // source file jrd/alt.c (as available on fourceforge.net/project/InterBase as
\r
37 // of 2000 Nov 12), it looks like the internal format of those EPB is simple.
\r
38 // An EPB starts by a byte with value 1. A version identifier of some sort.
\r
39 // Then a small cluster is used for each event name. The cluster starts with
\r
40 // a byte for the length of the event name (no final '\0'). Followed by the N
\r
41 // characters of the name itself (no final '\0'). The cluster ends with 4 bytes
\r
44 // SPECIAL CREDIT (July 2004) : this is a complete re-implementation of this
\r
45 // class, directly based on work by Val Samko.
\r
46 // The whole event handling has then be completely redesigned, based on the old
\r
47 // EPB class to bring up the current IBPP::Events implementation.
\r
49 ///////////////////////////////////////////////////////////////////////////////
\r
52 #pragma warning(disable: 4786 4996)
\r
54 #pragma warning(disable: 4702)
\r
64 using namespace ibpp_internals;
\r
66 const size_t EventsImpl::MAXEVENTNAMELEN = 127;
\r
68 // (((((((( OBJECT INTERFACE IMPLEMENTATION ))))))))
\r
70 void EventsImpl::Add(const std::string& eventname, IBPP::EventInterface* objref)
\r
72 if (eventname.size() == 0)
\r
73 throw LogicExceptionImpl("Events::Add", _("Zero length event names not permitted"));
\r
74 if (eventname.size() > MAXEVENTNAMELEN)
\r
75 throw LogicExceptionImpl("Events::Add", _("Event name is too long"));
\r
76 if ((mEventBuffer.size() + eventname.length() + 5) > 32766) // max signed 16 bits integer minus one
\r
77 throw LogicExceptionImpl("Events::Add",
\r
78 _("Can't add this event, the events list would overflow IB/FB limitation"));
\r
82 // 1) Alloc or grow the buffers
\r
83 size_t prev_buffer_size = mEventBuffer.size();
\r
84 size_t needed = ((prev_buffer_size==0) ? 1 : 0) + eventname.length() + 5;
\r
85 // Initial alloc will require one more byte, we need 4 more bytes for
\r
86 // the count itself, and one byte for the string length prefix
\r
88 mEventBuffer.resize(mEventBuffer.size() + needed);
\r
89 mResultsBuffer.resize(mResultsBuffer.size() + needed);
\r
90 if (prev_buffer_size == 0)
\r
91 mEventBuffer[0] = mResultsBuffer[0] = 1; // First byte is a 'one'. Documentation ??
\r
93 // 2) Update the buffers (append)
\r
95 Buffer::iterator it = mEventBuffer.begin() +
\r
96 ((prev_buffer_size==0) ? 1 : prev_buffer_size); // Byte after current content
\r
97 *(it++) = static_cast<char>(eventname.length());
\r
98 it = std::copy(eventname.begin(), eventname.end(), it);
\r
99 // We initialize the counts to (uint32_t)(-1) to initialize properly, see FireActions()
\r
100 *(it++) = -1; *(it++) = -1; *(it++) = -1; *it = -1;
\r
103 // copying new event to the results buffer to keep event_buffer_ and results_buffer_ consistant,
\r
104 // otherwise we might get a problem in `FireActions`
\r
105 // Val Samko, val@digiways.com
\r
106 std::copy(mEventBuffer.begin() + prev_buffer_size,
\r
107 mEventBuffer.end(), mResultsBuffer.begin() + prev_buffer_size);
\r
109 // 3) Alloc or grow the objref array and update the objref array (append)
\r
110 mObjectReferences.push_back(objref);
\r
115 void EventsImpl::Drop(const std::string& eventname)
\r
117 if (eventname.size() == 0)
\r
118 throw LogicExceptionImpl("EventsImpl::Drop", _("Zero length event names not permitted"));
\r
119 if (eventname.size() > MAXEVENTNAMELEN)
\r
120 throw LogicExceptionImpl("EventsImpl::Drop", _("Event name is too long"));
\r
122 if (mEventBuffer.size() <= 1) return; // Nothing to do, but not an error
\r
126 // 1) Find the event in the buffers
\r
127 typedef EventBufferIterator<Buffer::iterator> EventIterator;
\r
128 EventIterator eit(mEventBuffer.begin()+1);
\r
129 EventIterator rit(mResultsBuffer.begin()+1);
\r
131 for (ObjRefs::iterator oit = mObjectReferences.begin();
\r
132 oit != mObjectReferences.end();
\r
133 ++oit, ++eit, ++rit)
\r
135 if (eventname != eit.get_name()) continue;
\r
137 // 2) Event found, remove it
\r
138 mEventBuffer.erase(eit.begin(), eit.end());
\r
139 mResultsBuffer.erase(rit.begin(), rit.end());
\r
140 mObjectReferences.erase(oit);
\r
147 void EventsImpl::List(std::vector<std::string>& events)
\r
151 if (mEventBuffer.size() <= 1) return; // Nothing to do, but not an error
\r
153 typedef EventBufferIterator<Buffer::iterator> EventIterator;
\r
154 EventIterator eit(mEventBuffer.begin()+1);
\r
156 for (ObjRefs::iterator oit = mObjectReferences.begin();
\r
157 oit != mObjectReferences.end();
\r
160 events.push_back(eit.get_name());
\r
164 void EventsImpl::Clear()
\r
168 mObjectReferences.clear();
\r
169 mEventBuffer.clear();
\r
170 mResultsBuffer.clear();
\r
173 void EventsImpl::Dispatch()
\r
175 // If no events registered, nothing to do of course.
\r
176 if (mEventBuffer.size() == 0) return;
\r
178 // Let's fire the events actions for all the events which triggered, if any, and requeue.
\r
183 IBPP::Database EventsImpl::DatabasePtr() const
\r
185 if (mDatabase == 0) throw LogicExceptionImpl("Events::DatabasePtr",
\r
186 _("No Database is attached."));
\r
190 IBPP::IEvents* EventsImpl::AddRef()
\r
192 ASSERTION(mRefCount >= 0);
\r
197 void EventsImpl::Release()
\r
199 // Release cannot throw, except in DEBUG builds on assertion
\r
200 ASSERTION(mRefCount >= 0);
\r
202 try { if (mRefCount <= 0) delete this; }
\r
206 // (((((((( OBJECT INTERNAL METHODS ))))))))
\r
208 void EventsImpl::Queue()
\r
212 if (mDatabase->GetHandle() == 0)
\r
213 throw LogicExceptionImpl("EventsImpl::Queue",
\r
214 _("Database is not connected"));
\r
219 (*gds.Call()->m_que_events)(vector.Self(), mDatabase->GetHandlePtr(), &mId,
\r
220 short(mEventBuffer.size()), &mEventBuffer[0],
\r
221 (isc_callback)EventHandler, (char*)this);
\r
223 if (vector.Errors())
\r
225 mId = 0; // Should be, but better be safe
\r
227 throw SQLExceptionImpl(vector, "EventsImpl::Queue",
\r
228 _("isc_que_events failed"));
\r
233 void EventsImpl::Cancel()
\r
237 if (mDatabase->GetHandle() == 0) throw LogicExceptionImpl("EventsImpl::Cancel",
\r
238 _("Database is not connected"));
\r
242 // A call to cancel_events will call *once* the handler routine, even
\r
243 // though no events had fired. This is why we first set mEventsQueued
\r
244 // to false, so that we can be sure to dismiss those unwanted callbacks
\r
245 // subsequent to the execution of isc_cancel_events().
\r
248 (*gds.Call()->m_cancel_events)(vector.Self(), mDatabase->GetHandlePtr(), &mId);
\r
250 if (vector.Errors())
\r
252 mQueued = true; // Need to restore this as cancel failed
\r
253 throw SQLExceptionImpl(vector, "EventsImpl::Cancel",
\r
254 _("isc_cancel_events failed"));
\r
257 mId = 0; // Should be, but better be safe
\r
261 void EventsImpl::FireActions()
\r
265 typedef EventBufferIterator<Buffer::iterator> EventIterator;
\r
266 EventIterator eit(mEventBuffer.begin()+1);
\r
267 EventIterator rit(mResultsBuffer.begin()+1);
\r
269 for (ObjRefs::iterator oit = mObjectReferences.begin();
\r
270 oit != mObjectReferences.end();
\r
271 ++oit, ++eit, ++rit)
\r
273 if (eit == EventIterator(mEventBuffer.end())
\r
274 || rit == EventIterator(mResultsBuffer.end()))
\r
275 throw LogicExceptionImpl("EventsImpl::FireActions", _("Internal buffer size error"));
\r
276 uint32_t vnew = rit.get_count();
\r
277 uint32_t vold = eit.get_count();
\r
283 (*oit)->ibppEventHandler(this, eit.get_name(), (int)(vnew - vold));
\r
287 std::copy(rit.begin(), rit.end(), eit.begin());
\r
290 std::copy(rit.begin(), rit.end(), eit.begin());
\r
292 // This handles initialization too, where vold == (uint32_t)(-1)
\r
293 // Thanks to M. Hieke for this idea and related initialization to (-1)
\r
295 std::copy(rit.begin(), rit.end(), eit.begin());
\r
300 // This function must keep this prototype to stay compatible with
\r
301 // what isc_que_events() expects
\r
303 void EventsImpl::EventHandler(const char* object, short size, const char* tmpbuffer)
\r
305 // >>>>> This method is a STATIC member !! <<<<<
\r
306 // Consider this method as a kind of "interrupt handler". It should do as
\r
307 // few work as possible as quickly as possible and then return.
\r
308 // Never forget: this is called by the Firebird client code, on *some*
\r
309 // thread which might not be (and won't probably be) any of your application
\r
310 // thread. This function is to be considered as an "interrupt-handler" of a
\r
311 // hardware driver.
\r
313 // There can be spurious calls to EventHandler from FB internal. We must
\r
314 // dismiss those calls.
\r
315 if (object == 0 || size == 0 || tmpbuffer == 0) return;
\r
317 EventsImpl* evi = (EventsImpl*)object; // Ugly, but wanted, c-style cast
\r
323 char* rb = &evi->mResultsBuffer[0];
\r
324 if (evi->mEventBuffer.size() < (unsigned)size) size = (short)evi->mEventBuffer.size();
\r
325 for (int i = 0; i < size; i++)
\r
326 rb[i] = tmpbuffer[i];
\r
327 evi->mTrapped = true;
\r
328 evi->mQueued = false;
\r
334 void EventsImpl::AttachDatabaseImpl(DatabaseImpl* database)
\r
336 if (database == 0) throw LogicExceptionImpl("EventsImpl::AttachDatabase",
\r
337 _("Can't attach a null Database object."));
\r
339 if (mDatabase != 0) mDatabase->DetachEventsImpl(this);
\r
340 mDatabase = database;
\r
341 mDatabase->AttachEventsImpl(this);
\r
344 void EventsImpl::DetachDatabaseImpl()
\r
346 if (mDatabase == 0) return;
\r
348 mDatabase->DetachEventsImpl(this);
\r
352 EventsImpl::EventsImpl(DatabaseImpl* database)
\r
357 mQueued = mTrapped = false;
\r
358 AttachDatabaseImpl(database);
\r
361 EventsImpl::~EventsImpl()
\r
366 try { if (mDatabase != 0) mDatabase->DetachEventsImpl(this); }
\r