1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
"""Persistence support for Pottery
For now, this defines a service which holds a reference to the game's
realm instance. Periodically, the realm is pickled and written to
disk. When the service is created, if an existing pickled realm
exists on disk, it is loaded and used instead of the realm passed in.
"""
import os, errno
import cPickle as pickle
from twisted.application import service
from twisted.internet import task
from twisted.python import log
class PersistenceService(service.Service):
def __init__(self, fname, realm):
self.fname = fname
if os.path.exists(fname):
realm = self._unpersist()
self.realm = realm
def startService(self):
self.loop = task.LoopingCall(self._persist)
self.loop.start(60).addErrback(log.err)
def stopService(self):
self.loop.stop()
self._persist()
def _persist(self):
# Try to reap the previous persistence child.
try:
os.waitpid(-1, os.WNOHANG)
except OSError, e:
if e.errno != errno.ECHILD:
raise
# Fork and continue normal execution in the parent. In the child,
# try to acquire the persist lock. If it cannot be acquired,
# return. Otherwise, pickle game state to a new file, move the
# entire result over the existing game state file, and release the
# lock.
pid = os.fork()
if pid == 0:
try:
try:
os.mkdir(self.fname + '.lock')
except OSError, e:
if e.errno != errno.EEXIST:
raise
else:
try:
fObj = file(self.fname + '.tmp', 'wb')
fObj.write(pickle.dumps(self.realm))
fObj.close()
os.rename(self.fname + '.tmp', self.fname)
finally:
os.rmdir(self.fname + '.lock')
finally:
os._exit(0)
def _unpersist(self):
fObj = file(self.fname, 'rb')
return pickle.loads(fObj.read())
|