Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'python/src/tcf/util/cache.py')
-rw-r--r--python/src/tcf/util/cache.py540
1 files changed, 270 insertions, 270 deletions
diff --git a/python/src/tcf/util/cache.py b/python/src/tcf/util/cache.py
index aaa8622c3..7e33c535e 100644
--- a/python/src/tcf/util/cache.py
+++ b/python/src/tcf/util/cache.py
@@ -1,270 +1,270 @@
-# *******************************************************************************
-# * Copyright (c) 2011 Wind River Systems, Inc. and others.
-# * All rights reserved. This program and the accompanying materials
-# * are made available under the terms of the Eclipse Public License v1.0
-# * which accompanies this distribution, and is available at
-# * http://www.eclipse.org/legal/epl-v10.html
-# *
-# * Contributors:
-# * Wind River Systems - initial API and implementation
-# *******************************************************************************
-
-import cStringIO
-from tcf import protocol, channel
-
-class DataCache(object):
- """
- Objects of this class are used to cache TCF remote data.
- The cache is asynchronous state machine. The states are:
- 1. Valid - cache is in sync with remote data, use getError() and getData() to get cached data
- 2. Invalid - cache is out of sync, start data retrieval by calling validate()
- 3. Pending - cache is waiting result of a command that was sent to remote peer
- 4. Disposed - cache is disposed and cannot be used to store data.
-
- A cache instance can be created on any data type that needs to be cached.
- Examples might be context children list, context properties, context state, memory data,
- register data, symbol, variable, etc.
- Clients of cache items can register for cache changes, but don't need to think about any particular events
- since that is handled by the cache item itself.
-
- A typical cache client should implement Runnable interface.
- The implementation of run() method should:
-
- Validate all cache items required for client task.
- If anything is invalid then client should not alter any shared data structures,
- should discard any intermediate results and register (wait) for changes of invalid cache instance(s) state.
- When cache item state changes, client is invoked again and full validation is restarted.
- Once everything is valid, client completes its task in a single dispatch cycle.
-
- Note: clients should never retain copies of remote data across dispatch cycles!
- Such data would get out of sync and compromise data consistency.
- All remote data and everything derived from remote data should be kept in cache items
- that implement proper event handling and can keep data consistent across dispatch cycles.
- """
-
- __error = None
- __valid = None
- __posted = False
- __disposed = False
- __data = None
-
- def __init__(self, channel):
- assert channel
- self._channel = channel
- self._command = None
- self.__waiting_list = None
-
- def post(self):
- if self.__posted: return
- if not self.__waiting_list: return
- protocol.invokeLater(self)
- self.__posted = True
-
- def isValid(self):
- """
- @return True if cache contains up-to-date data or error.
- """
- return self.__valid
-
- def isPending(self):
- """
- @return True if data retrieval command is in progress.
- """
- return self._command is not None
-
- def isDisposed(self):
- """
- @return True if cache is disposed.
- """
- return self.__disposed
-
- def getError(self):
- """
- @return error object if data retrieval ended with an error, or None if retrieval was successful.
- Note: It is prohibited to call this method when cache is not valid.
- """
- assert self.__valid
- return self.__error
-
- def getData(self):
- """
- @return cached data object.
- Note: It is prohibited to call this method when cache is not valid.
- """
- assert protocol.isDispatchThread()
- assert self.__valid
- return self.__data
-
- def __call__(self):
- """
- Notify waiting clients about cache state change and remove them from wait list.
- It is responsibility of clients to check if the state change was one they are waiting for.
- Clients are not intended to call this method.
- """
- assert protocol.isDispatchThread()
- self.__posted = False
- if self.__waiting_list:
- arr = self.__waiting_list
- self.__waiting_list = None
- for r in arr:
- if isinstance(r, DataCache) and r._DataCache__posted: continue
- r()
- if self.__waiting_list is None: self.__waiting_list = arr
-
- def wait(self, cb):
- """
- Add a client call-back to cache wait list.
- Client call-backs are activated when cache state changes.
- Call-backs are removed from waiting list after that.
- It is responsibility of clients to check if the state change was one they are waiting for.
- @param cb - a call-back object
- """
- assert protocol.isDispatchThread()
- assert not self.__disposed
- assert not self.__valid
- if cb and not self.isWaiting(cb):
- if self.__waiting_list is None: self.__waiting_list = []
- self.__waiting_list.append(cb)
-
- def isWaiting(self, cb):
- """
- Return True if a client call-back is waiting for state changes of this cache item.
- @param cb - a call-back object.
- @return True if 'cb' is in the wait list.
- """
- if not self.__waiting_list: return False
- for r in self.__waiting_list:
- if r is cb: return True
- return False
-
- def __validate(self):
- """
- Initiate data retrieval if the cache is not valid.
- @return True if the cache is already valid
- """
- assert protocol.isDispatchThread()
- if self.__disposed or self._channel.getState() != channel.STATE_OPEN:
- self._command = None
- self.__valid = True
- self.__error = None
- self.__data = None
- else:
- if self._command is not None: return False
- if not self.__valid and not self.startDataRetrieval(): return False
- assert self.__valid
- assert self._command is None
- self.post()
- return True
-
- def validate(self, cb=None):
- """
- If the cache is not valid, initiate data retrieval and
- add a client call-back to cache wait list.
- Client call-backs are activated when cache state changes.
- Call-backs are removed from waiting list after that.
- It is responsibility of clients to check if the state change was one they are waiting for.
- If the cache is valid do nothing and return True.
- @param cb - a call-back object (optional)
- @return True if the cache is already valid
- """
- if not self.__validate():
- if cb: self.wait(cb)
- return False
- return True
-
- def start(self, command):
- """
- Start cache pending state.
- Pending state is when the cache is waiting for a TCF command to return results.
- @param command - TCF command handle.
- """
- assert not self.__valid
- assert command
- assert self._command is None
- self._command = command
-
- def done(self, command):
- """
- End cache pending state, but not mark the cache as valid.
- @param command - TCF command handle.
- """
- if self._command is not command: return
- assert not self.__valid
- self._command = None
- self.post()
-
- def set(self, token, error, data):
- """
- End cache pending state and mark the cache as valid.
- If 'token' != None, the data represent results from a completed command.
- The data should be ignored if current cache pending command is not same as 'token'.
- It can happen if the cache was reset or canceled during data retrieval.
- @param token - pending command handle or None.
- @param error - data retrieval error or None
- @param data - up-to-date data object
- """
- assert protocol.isDispatchThread()
- if token and self._command is not token: return
- self._command = None
- if not self.__disposed:
- assert not self.__valid
- if self._channel.getState() != channel.STATE_OPEN:
- self.__error = None
- self.__data = None
- self.__error = error
- self.__data = data
- self.__valid = True
- self.post()
-
- def reset(self, data=None):
- """
- Force cache to become valid, cancel pending data retrieval if data is provided.
- @param data - up-to-date data object (optional)
- """
- assert protocol.isDispatchThread()
- if data is not None and self._command is not None:
- self._command.cancel()
- self._command = None
- if not self.__disposed:
- self.__data = data
- self.__error = None
- self.__valid = True
- self.post()
-
- def cancel(self):
- """
- Invalidate the cache.
- Cancel pending data retrieval if any.
- """
- self.reset()
- if self._command is not None:
- self._command.cancel()
- self._command = None
-
- def dispose(self):
- """
- Dispose the cache.
- Cancel pending data retrieval if any.
- """
- self.cancel()
- self.__valid = True
- self.__disposed = True
-
- def __str__(self):
- bf = cStringIO.StringIO()
- bf.write('[')
- if self.__valid: bf.append("valid,")
- if self.__disposed: bf.write("disposed,")
- if self.__posted: bf.write("posted,")
- if self.__error is not None: bf.write("error,")
- bf.write("data=")
- bf.write(str(self.__data))
- bf.write(']')
- return bf.getvalue()
-
- def startDataRetrieval(self):
- """
- Sub-classes should override this method to implement actual data retrieval logic.
- @return True is all done, False if retrieval is in progress.
- """
- raise NotImplementedError("Abstract method")
+# *******************************************************************************
+# * Copyright (c) 2011 Wind River Systems, Inc. and others.
+# * All rights reserved. This program and the accompanying materials
+# * are made available under the terms of the Eclipse Public License v1.0
+# * which accompanies this distribution, and is available at
+# * http://www.eclipse.org/legal/epl-v10.html
+# *
+# * Contributors:
+# * Wind River Systems - initial API and implementation
+# *******************************************************************************
+
+import cStringIO
+from tcf import protocol, channel
+
+class DataCache(object):
+ """
+ Objects of this class are used to cache TCF remote data.
+ The cache is asynchronous state machine. The states are:
+ 1. Valid - cache is in sync with remote data, use getError() and getData() to get cached data
+ 2. Invalid - cache is out of sync, start data retrieval by calling validate()
+ 3. Pending - cache is waiting result of a command that was sent to remote peer
+ 4. Disposed - cache is disposed and cannot be used to store data.
+
+ A cache instance can be created on any data type that needs to be cached.
+ Examples might be context children list, context properties, context state, memory data,
+ register data, symbol, variable, etc.
+ Clients of cache items can register for cache changes, but don't need to think about any particular events
+ since that is handled by the cache item itself.
+
+ A typical cache client should implement Runnable interface.
+ The implementation of run() method should:
+
+ Validate all cache items required for client task.
+ If anything is invalid then client should not alter any shared data structures,
+ should discard any intermediate results and register (wait) for changes of invalid cache instance(s) state.
+ When cache item state changes, client is invoked again and full validation is restarted.
+ Once everything is valid, client completes its task in a single dispatch cycle.
+
+ Note: clients should never retain copies of remote data across dispatch cycles!
+ Such data would get out of sync and compromise data consistency.
+ All remote data and everything derived from remote data should be kept in cache items
+ that implement proper event handling and can keep data consistent across dispatch cycles.
+ """
+
+ __error = None
+ __valid = None
+ __posted = False
+ __disposed = False
+ __data = None
+
+ def __init__(self, channel):
+ assert channel
+ self._channel = channel
+ self._command = None
+ self.__waiting_list = None
+
+ def post(self):
+ if self.__posted: return
+ if not self.__waiting_list: return
+ protocol.invokeLater(self)
+ self.__posted = True
+
+ def isValid(self):
+ """
+ @return True if cache contains up-to-date data or error.
+ """
+ return self.__valid
+
+ def isPending(self):
+ """
+ @return True if data retrieval command is in progress.
+ """
+ return self._command is not None
+
+ def isDisposed(self):
+ """
+ @return True if cache is disposed.
+ """
+ return self.__disposed
+
+ def getError(self):
+ """
+ @return error object if data retrieval ended with an error, or None if retrieval was successful.
+ Note: It is prohibited to call this method when cache is not valid.
+ """
+ assert self.__valid
+ return self.__error
+
+ def getData(self):
+ """
+ @return cached data object.
+ Note: It is prohibited to call this method when cache is not valid.
+ """
+ assert protocol.isDispatchThread()
+ assert self.__valid
+ return self.__data
+
+ def __call__(self):
+ """
+ Notify waiting clients about cache state change and remove them from wait list.
+ It is responsibility of clients to check if the state change was one they are waiting for.
+ Clients are not intended to call this method.
+ """
+ assert protocol.isDispatchThread()
+ self.__posted = False
+ if self.__waiting_list:
+ arr = self.__waiting_list
+ self.__waiting_list = None
+ for r in arr:
+ if isinstance(r, DataCache) and r._DataCache__posted: continue
+ r()
+ if self.__waiting_list is None: self.__waiting_list = arr
+
+ def wait(self, cb):
+ """
+ Add a client call-back to cache wait list.
+ Client call-backs are activated when cache state changes.
+ Call-backs are removed from waiting list after that.
+ It is responsibility of clients to check if the state change was one they are waiting for.
+ @param cb - a call-back object
+ """
+ assert protocol.isDispatchThread()
+ assert not self.__disposed
+ assert not self.__valid
+ if cb and not self.isWaiting(cb):
+ if self.__waiting_list is None: self.__waiting_list = []
+ self.__waiting_list.append(cb)
+
+ def isWaiting(self, cb):
+ """
+ Return True if a client call-back is waiting for state changes of this cache item.
+ @param cb - a call-back object.
+ @return True if 'cb' is in the wait list.
+ """
+ if not self.__waiting_list: return False
+ for r in self.__waiting_list:
+ if r is cb: return True
+ return False
+
+ def __validate(self):
+ """
+ Initiate data retrieval if the cache is not valid.
+ @return True if the cache is already valid
+ """
+ assert protocol.isDispatchThread()
+ if self.__disposed or self._channel.getState() != channel.STATE_OPEN:
+ self._command = None
+ self.__valid = True
+ self.__error = None
+ self.__data = None
+ else:
+ if self._command is not None: return False
+ if not self.__valid and not self.startDataRetrieval(): return False
+ assert self.__valid
+ assert self._command is None
+ self.post()
+ return True
+
+ def validate(self, cb=None):
+ """
+ If the cache is not valid, initiate data retrieval and
+ add a client call-back to cache wait list.
+ Client call-backs are activated when cache state changes.
+ Call-backs are removed from waiting list after that.
+ It is responsibility of clients to check if the state change was one they are waiting for.
+ If the cache is valid do nothing and return True.
+ @param cb - a call-back object (optional)
+ @return True if the cache is already valid
+ """
+ if not self.__validate():
+ if cb: self.wait(cb)
+ return False
+ return True
+
+ def start(self, command):
+ """
+ Start cache pending state.
+ Pending state is when the cache is waiting for a TCF command to return results.
+ @param command - TCF command handle.
+ """
+ assert not self.__valid
+ assert command
+ assert self._command is None
+ self._command = command
+
+ def done(self, command):
+ """
+ End cache pending state, but not mark the cache as valid.
+ @param command - TCF command handle.
+ """
+ if self._command is not command: return
+ assert not self.__valid
+ self._command = None
+ self.post()
+
+ def set(self, token, error, data):
+ """
+ End cache pending state and mark the cache as valid.
+ If 'token' != None, the data represent results from a completed command.
+ The data should be ignored if current cache pending command is not same as 'token'.
+ It can happen if the cache was reset or canceled during data retrieval.
+ @param token - pending command handle or None.
+ @param error - data retrieval error or None
+ @param data - up-to-date data object
+ """
+ assert protocol.isDispatchThread()
+ if token and self._command is not token: return
+ self._command = None
+ if not self.__disposed:
+ assert not self.__valid
+ if self._channel.getState() != channel.STATE_OPEN:
+ self.__error = None
+ self.__data = None
+ self.__error = error
+ self.__data = data
+ self.__valid = True
+ self.post()
+
+ def reset(self, data=None):
+ """
+ Force cache to become valid, cancel pending data retrieval if data is provided.
+ @param data - up-to-date data object (optional)
+ """
+ assert protocol.isDispatchThread()
+ if data is not None and self._command is not None:
+ self._command.cancel()
+ self._command = None
+ if not self.__disposed:
+ self.__data = data
+ self.__error = None
+ self.__valid = True
+ self.post()
+
+ def cancel(self):
+ """
+ Invalidate the cache.
+ Cancel pending data retrieval if any.
+ """
+ self.reset()
+ if self._command is not None:
+ self._command.cancel()
+ self._command = None
+
+ def dispose(self):
+ """
+ Dispose the cache.
+ Cancel pending data retrieval if any.
+ """
+ self.cancel()
+ self.__valid = True
+ self.__disposed = True
+
+ def __str__(self):
+ bf = cStringIO.StringIO()
+ bf.write('[')
+ if self.__valid: bf.append("valid,")
+ if self.__disposed: bf.write("disposed,")
+ if self.__posted: bf.write("posted,")
+ if self.__error is not None: bf.write("error,")
+ bf.write("data=")
+ bf.write(str(self.__data))
+ bf.write(']')
+ return bf.getvalue()
+
+ def startDataRetrieval(self):
+ """
+ Sub-classes should override this method to implement actual data retrieval logic.
+ @return True is all done, False if retrieval is in progress.
+ """
+ raise NotImplementedError("Abstract method")

Back to the top