Made ChannelDBHandler threadsafe, and wrote a DBHandler for threadsafe non-channel-based databases.

This commit is contained in:
Jeremy Fincher 2003-11-15 07:56:27 +00:00
parent dc93f865db
commit 27ce432b64
4 changed files with 162 additions and 94 deletions

View File

@ -59,7 +59,7 @@ import callbacks
import Owner import Owner
dbfilename = os.path.join(conf.dataDir, 'MoobotFactoids.db') dbfilename = os.path.join(conf.dataDir, 'MoobotFactoids')
def configure(onStart, afterConnect, advanced): def configure(onStart, afterConnect, advanced):
# This will be called by setup.py to configure this module. onStart and # This will be called by setup.py to configure this module. onStart and
@ -118,41 +118,46 @@ class OptionList(object):
def pickOptions(s): def pickOptions(s):
return OptionList().tokenize(s) return OptionList().tokenize(s)
class MoobotDBHandler(plugins.DBHandler):
def makeDb(self, filename):
"""create MoobotFactoids database and tables"""
if os.path.exists(filename):
db = sqlite.connect(filename)
else:
db = sqlite.connect(filename, converters={'bool': bool})
cursor = db.cursor()
cursor.execute("""CREATE TABLE factoids (
key TEXT PRIMARY KEY,
created_by INTEGER,
created_at TIMESTAMP,
modified_by INTEGER,
modified_at TIMESTAMP,
locked_at TIMESTAMP,
locked_by INTEGER,
last_requested_by TEXT,
last_requested_at TIMESTAMP,
fact TEXT,
requested_count INTEGER
)""")
db.commit()
return db
class MoobotFactoids(callbacks.PrivmsgCommandAndRegexp): class MoobotFactoids(callbacks.PrivmsgCommandAndRegexp):
priority = 98 priority = 98
addressedRegexps = ['changeFactoid', 'augmentFactoid', addressedRegexps = ['changeFactoid', 'augmentFactoid',
'replaceFactoid', 'addFactoid'] 'replaceFactoid', 'addFactoid']
def __init__(self): def __init__(self):
callbacks.PrivmsgCommandAndRegexp.__init__(self) callbacks.PrivmsgCommandAndRegexp.__init__(self)
self.makeDb(dbfilename) self.dbHandler = MoobotDBHandler(dbfilename)
def makeDb(self, filename):
"""create MoobotFactoids database and tables"""
if os.path.exists(filename):
self.db = sqlite.connect(filename)
return
self.db = sqlite.connect(filename, converters={'bool': bool})
cursor = self.db.cursor()
cursor.execute("""CREATE TABLE factoids (
key TEXT PRIMARY KEY,
created_by INTEGER,
created_at TIMESTAMP,
modified_by INTEGER,
modified_at TIMESTAMP,
locked_at TIMESTAMP,
locked_by INTEGER,
last_requested_by TEXT,
last_requested_at TIMESTAMP,
fact TEXT,
requested_count INTEGER
)""")
self.db.commit()
def die(self): def die(self):
# Handle DB stuff # Handle DB stuff
self.db.commit() db = self.dbHandler.getDb()
self.db.close() db.commit()
del self.db db.close()
del db
def parseFactoid(self, irc, msg, fact): def parseFactoid(self, irc, msg, fact):
type = "define" # Default is to just spit the factoid back as a type = "define" # Default is to just spit the factoid back as a
@ -171,14 +176,15 @@ class MoobotFactoids(callbacks.PrivmsgCommandAndRegexp):
def updateFactoidRequest(self, key, hostmask): def updateFactoidRequest(self, key, hostmask):
"""Updates the last_requested_* fields of a factoid row""" """Updates the last_requested_* fields of a factoid row"""
cursor = self.db.cursor() db = self.dbHandler.getDb()
cursor = db.cursor()
cursor.execute("""UPDATE factoids SET cursor.execute("""UPDATE factoids SET
last_requested_by = %s, last_requested_by = %s,
last_requested_at = %s, last_requested_at = %s,
requested_count = requested_count +1 requested_count = requested_count +1
WHERE key = %s""", WHERE key = %s""",
hostmask, int(time.time()), key) hostmask, int(time.time()), key)
self.db.commit() db.commit()
def invalidCommand(self, irc, msg, tokens): def invalidCommand(self, irc, msg, tokens):
key = ' '.join(tokens) key = ' '.join(tokens)
@ -186,7 +192,8 @@ class MoobotFactoids(callbacks.PrivmsgCommandAndRegexp):
if key.startswith('\x01'): if key.startswith('\x01'):
return return
# Check the factoid db for an appropriate reply # Check the factoid db for an appropriate reply
cursor = self.db.cursor() db = self.dbHandler.getDb()
cursor = db.cursor()
cursor.execute("""SELECT fact FROM factoids WHERE key LIKE %s""", key) cursor.execute("""SELECT fact FROM factoids WHERE key LIKE %s""", key)
if cursor.rowcount == 0: if cursor.rowcount == 0:
return False return False
@ -210,7 +217,8 @@ class MoobotFactoids(callbacks.PrivmsgCommandAndRegexp):
def addFactoid(self, irc, msg, match): def addFactoid(self, irc, msg, match):
r"^(.+?)\s+(?:is|_is_)\s+(.+)" r"^(.+?)\s+(?:is|_is_)\s+(.+)"
# First, check and see if the entire message matches a factoid key # First, check and see if the entire message matches a factoid key
cursor = self.db.cursor() db = self.dbHandler.getDb()
cursor = db.cursor()
cursor.execute("""SELECT * FROM factoids WHERE key LIKE %s""", cursor.execute("""SELECT * FROM factoids WHERE key LIKE %s""",
match.group().rstrip('?! ')) match.group().rstrip('?! '))
if cursor.rowcount != 0: if cursor.rowcount != 0:
@ -240,7 +248,7 @@ class MoobotFactoids(callbacks.PrivmsgCommandAndRegexp):
(%s, %s, %s, NULL, NULL, NULL, NULL, NULL, NULL, (%s, %s, %s, NULL, NULL, NULL, NULL, NULL, NULL,
%s, 0)""", %s, 0)""",
key, id, int(time.time()), fact) key, id, int(time.time()), fact)
self.db.commit() db.commit()
irc.reply(msg, conf.replySuccess) irc.reply(msg, conf.replySuccess)
def changeFactoid(self, irc, msg, match): def changeFactoid(self, irc, msg, match):
@ -252,7 +260,8 @@ class MoobotFactoids(callbacks.PrivmsgCommandAndRegexp):
irc.error(msg, conf.replyNotRegistered) irc.error(msg, conf.replyNotRegistered)
return return
key, regexp = match.groups() key, regexp = match.groups()
cursor = self.db.cursor() db = self.dbHandler.getDb()
cursor = db.cursor()
# Check and make sure it's in the DB # Check and make sure it's in the DB
cursor.execute("""SELECT locked_at, fact FROM factoids cursor.execute("""SELECT locked_at, fact FROM factoids
WHERE key LIKE %s""", key) WHERE key LIKE %s""", key)
@ -275,7 +284,7 @@ class MoobotFactoids(callbacks.PrivmsgCommandAndRegexp):
SET fact = %s, modified_by = %s, SET fact = %s, modified_by = %s,
modified_at = %s WHERE key = %s""", modified_at = %s WHERE key = %s""",
new_fact, id, int(time.time()), key) new_fact, id, int(time.time()), key)
self.db.commit() db.commit()
irc.reply(msg, conf.replySuccess) irc.reply(msg, conf.replySuccess)
def augmentFactoid(self, irc, msg, match): def augmentFactoid(self, irc, msg, match):
@ -287,7 +296,8 @@ class MoobotFactoids(callbacks.PrivmsgCommandAndRegexp):
irc.error(msg, conf.replyNotRegistered) irc.error(msg, conf.replyNotRegistered)
return return
key, new_text = match.groups() key, new_text = match.groups()
cursor = self.db.cursor() db = self.dbHandler.getDb()
cursor = db.cursor()
# Check and make sure it's in the DB # Check and make sure it's in the DB
cursor.execute("""SELECT locked_at, fact FROM factoids cursor.execute("""SELECT locked_at, fact FROM factoids
WHERE key LIKE %s""", key) WHERE key LIKE %s""", key)
@ -305,7 +315,7 @@ class MoobotFactoids(callbacks.PrivmsgCommandAndRegexp):
SET fact = %s, modified_by = %s, SET fact = %s, modified_by = %s,
modified_at = %s WHERE key = %s""", modified_at = %s WHERE key = %s""",
new_fact, id, int(time.time()), key) new_fact, id, int(time.time()), key)
self.db.commit() db.commit()
irc.reply(msg, conf.replySuccess) irc.reply(msg, conf.replySuccess)
def replaceFactoid(self, irc, msg, match): def replaceFactoid(self, irc, msg, match):
@ -322,7 +332,8 @@ class MoobotFactoids(callbacks.PrivmsgCommandAndRegexp):
if '_is_' in match.group(): if '_is_' in match.group():
key, new_fact = imap(str.strip, match.group().split('_is_', 1)) key, new_fact = imap(str.strip, match.group().split('_is_', 1))
key = key.split(' ', 1)[1] # Take out everything to first space key = key.split(' ', 1)[1] # Take out everything to first space
cursor = self.db.cursor() db = self.dbHandler.getDb()
cursor = db.cursor()
# Check and make sure it's in the DB # Check and make sure it's in the DB
cursor.execute("""SELECT locked_at, fact FROM factoids cursor.execute("""SELECT locked_at, fact FROM factoids
WHERE key LIKE %s""", key) WHERE key LIKE %s""", key)
@ -343,7 +354,7 @@ class MoobotFactoids(callbacks.PrivmsgCommandAndRegexp):
locked_at = NULL locked_at = NULL
WHERE key = %s""", WHERE key = %s""",
new_fact, id, int(time.time()), key) new_fact, id, int(time.time()), key)
self.db.commit() db.commit()
irc.reply(msg, conf.replySuccess) irc.reply(msg, conf.replySuccess)
def literal(self, irc, msg, args): def literal(self, irc, msg, args):
@ -353,7 +364,8 @@ class MoobotFactoids(callbacks.PrivmsgCommandAndRegexp):
the factoid value is done as it is with normal retrieval. the factoid value is done as it is with normal retrieval.
""" """
key = privmsgs.getArgs(args, required=1) key = privmsgs.getArgs(args, required=1)
cursor = self.db.cursor() db = self.dbHandler.getDb()
cursor = db.cursor()
cursor.execute("""SELECT fact FROM factoids WHERE key LIKE %s""", key) cursor.execute("""SELECT fact FROM factoids WHERE key LIKE %s""", key)
if cursor.rowcount == 0: if cursor.rowcount == 0:
irc.error(msg, "No such factoid: %r" % key) irc.error(msg, "No such factoid: %r" % key)
@ -371,7 +383,8 @@ class MoobotFactoids(callbacks.PrivmsgCommandAndRegexp):
# Start building the response string # Start building the response string
s = key + ": " s = key + ": "
# Next, get all the info and build the response piece by piece # Next, get all the info and build the response piece by piece
cursor = self.db.cursor() db = self.dbHandler.getDb()
cursor = db.cursor()
cursor.execute("""SELECT created_by, created_at, modified_by, cursor.execute("""SELECT created_by, created_at, modified_by,
modified_at, last_requested_by, last_requested_at, modified_at, last_requested_by, last_requested_at,
requested_count, locked_by, locked_at FROM requested_count, locked_by, locked_at FROM
@ -418,7 +431,8 @@ class MoobotFactoids(callbacks.PrivmsgCommandAndRegexp):
irc.error(msg, conf.replyNotRegistered) irc.error(msg, conf.replyNotRegistered)
return return
key = privmsgs.getArgs(args, required=1) key = privmsgs.getArgs(args, required=1)
cursor = self.db.cursor() db = self.dbHandler.getDb()
cursor = db.cursor()
cursor.execute("""SELECT created_by, locked_by FROM factoids cursor.execute("""SELECT created_by, locked_by FROM factoids
WHERE key LIKE %s""", key) WHERE key LIKE %s""", key)
if cursor.rowcount == 0: if cursor.rowcount == 0:
@ -450,7 +464,7 @@ class MoobotFactoids(callbacks.PrivmsgCommandAndRegexp):
id = None id = None
cursor.execute("""UPDATE factoids SET locked_at = %s, locked_by = %s cursor.execute("""UPDATE factoids SET locked_at = %s, locked_by = %s
WHERE key = %s""", locked_at, id, key) WHERE key = %s""", locked_at, id, key)
self.db.commit() db.commit()
irc.reply(msg, conf.replySuccess) irc.reply(msg, conf.replySuccess)
def lock(self, irc, msg, args): def lock(self, irc, msg, args):
@ -484,7 +498,8 @@ class MoobotFactoids(callbacks.PrivmsgCommandAndRegexp):
method = getattr(self, '_most%s' % arg, None) method = getattr(self, '_most%s' % arg, None)
if method is None: if method is None:
raise callbacks.ArgumentError raise callbacks.ArgumentError
cursor = self.db.cursor() db = self.dbHandler.getDb()
cursor = db.cursor()
cursor.execute("""SELECT COUNT(*) FROM factoids""") cursor.execute("""SELECT COUNT(*) FROM factoids""")
if int(cursor.fetchone()[0]) == 0: if int(cursor.fetchone()[0]) == 0:
irc.error(msg, 'I don\'t have any factoids in my database!') irc.error(msg, 'I don\'t have any factoids in my database!')
@ -535,7 +550,8 @@ class MoobotFactoids(callbacks.PrivmsgCommandAndRegexp):
except KeyError: except KeyError:
irc.error(msg, "No such user: %r" % author) irc.error(msg, "No such user: %r" % author)
return return
cursor = self.db.cursor() db = self.dbHandler.getDb()
cursor = db.cursor()
cursor.execute("""SELECT key FROM factoids cursor.execute("""SELECT key FROM factoids
WHERE created_by = %s WHERE created_by = %s
ORDER BY key""", id) ORDER BY key""", id)
@ -554,7 +570,8 @@ class MoobotFactoids(callbacks.PrivmsgCommandAndRegexp):
""" """
search = privmsgs.getArgs(args, required=1) search = privmsgs.getArgs(args, required=1)
glob = '%' + search + '%' glob = '%' + search + '%'
cursor = self.db.cursor() db = self.dbHandler.getDb()
cursor = db.cursor()
cursor.execute("""SELECT key FROM factoids cursor.execute("""SELECT key FROM factoids
WHERE key LIKE %s WHERE key LIKE %s
ORDER BY key""", ORDER BY key""",
@ -574,7 +591,8 @@ class MoobotFactoids(callbacks.PrivmsgCommandAndRegexp):
""" """
search = privmsgs.getArgs(args, required=1) search = privmsgs.getArgs(args, required=1)
glob = '%' + search + '%' glob = '%' + search + '%'
cursor = self.db.cursor() db = self.dbHandler.getDb()
cursor = db.cursor()
cursor.execute("""SELECT key FROM factoids cursor.execute("""SELECT key FROM factoids
WHERE fact LIKE %s WHERE fact LIKE %s
ORDER BY key""", ORDER BY key""",
@ -599,7 +617,8 @@ class MoobotFactoids(callbacks.PrivmsgCommandAndRegexp):
irc.error(msg, conf.replyNotRegistered) irc.error(msg, conf.replyNotRegistered)
return return
key = privmsgs.getArgs(args, required=1) key = privmsgs.getArgs(args, required=1)
cursor = self.db.cursor() db = self.dbHandler.getDb()
cursor = db.cursor()
cursor.execute("""SELECT key, locked_at FROM factoids cursor.execute("""SELECT key, locked_at FROM factoids
WHERE key LIKE %s""", key) WHERE key LIKE %s""", key)
if cursor.rowcount == 0: if cursor.rowcount == 0:
@ -610,7 +629,7 @@ class MoobotFactoids(callbacks.PrivmsgCommandAndRegexp):
irc.error(msg, "Factoid is locked, cannot remove.") irc.error(msg, "Factoid is locked, cannot remove.")
return return
cursor.execute("""DELETE FROM factoids WHERE key = %s""", key) cursor.execute("""DELETE FROM factoids WHERE key = %s""", key)
self.db.commit() db.commit()
irc.reply(msg, conf.replySuccess) irc.reply(msg, conf.replySuccess)
Class = MoobotFactoids Class = MoobotFactoids

View File

@ -47,54 +47,62 @@ import debug
import utils import utils
import ircdb import ircdb
import ircmsgs import ircmsgs
import plugins
import privmsgs import privmsgs
import ircutils import ircutils
import callbacks import callbacks
dbfilename = os.path.join(conf.dataDir, 'Notes.db') dbfilename = os.path.join(conf.dataDir, 'Notes.db')
class NoteDb(plugins.DBHandler):
def makeDb(self, filename):
"create Notes database and tables"
if os.path.exists(filename):
db = sqlite.connect(filename)
else:
db = sqlite.connect(filename, converters={'bool': bool})
cursor = db.cursor()
cursor.execute("""CREATE TABLE notes (
id INTEGER PRIMARY KEY,
from_id INTEGER,
to_id INTEGER,
added_at TIMESTAMP,
notified BOOLEAN,
read BOOLEAN,
public BOOLEAN,
note TEXT
)""")
db.commit()
return db
class Note(callbacks.Privmsg): class Note(callbacks.Privmsg):
def __init__(self): def __init__(self):
callbacks.Privmsg.__init__(self) callbacks.Privmsg.__init__(self)
self.makeDB(dbfilename) self.dbHandler = NoteDb(name=os.path.join(conf.dataDir, 'Notes'))
def makeDB(self, filename):
"create Notes database and tables"
if os.path.exists(filename):
self.db = sqlite.connect(filename)
return
self.db = sqlite.connect(filename, converters={'bool': bool})
cursor = self.db.cursor()
cursor.execute("""CREATE TABLE notes (
id INTEGER PRIMARY KEY,
from_id INTEGER,
to_id INTEGER,
added_at TIMESTAMP,
notified BOOLEAN,
read BOOLEAN,
public BOOLEAN,
note TEXT
)""")
self.db.commit()
def setAsRead(self, id): def setAsRead(self, id):
cursor = self.db.cursor() db = self.dbHandler.getDb()
cursor = db.cursor()
cursor.execute("""UPDATE notes cursor.execute("""UPDATE notes
SET read=1, notified=1 SET read=1, notified=1
WHERE id=%s""", id) WHERE id=%s""", id)
self.db.commit() db.commit()
def die(self): def die(self):
self.db.commit() db = self.dbHandler.getDb()
self.db.close() db.commit()
del self.db db.close()
del db
def doPrivmsg(self, irc, msg): def doPrivmsg(self, irc, msg):
try: try:
id = ircdb.users.getUserId(msg.prefix) id = ircdb.users.getUserId(msg.prefix)
except KeyError: except KeyError:
return return
cursor = self.db.cursor() db = self.dbHandler.getDb()
cursor = db.cursor()
cursor.execute("""SELECT COUNT(*) FROM notes cursor.execute("""SELECT COUNT(*) FROM notes
WHERE notes.to_id=%s AND notified=0""", id) WHERE notes.to_id=%s AND notified=0""", id)
unnotified = int(cursor.fetchone()[0]) unnotified = int(cursor.fetchone()[0])
@ -108,7 +116,7 @@ class Note(callbacks.Privmsg):
irc.queueMsg(ircmsgs.privmsg(msg.nick, s)) irc.queueMsg(ircmsgs.privmsg(msg.nick, s))
cursor.execute("""UPDATE notes SET notified=1 cursor.execute("""UPDATE notes SET notified=1
WHERE notes.to_id=%s""", id) WHERE notes.to_id=%s""", id)
self.db.commit() db.commit()
def send(self, irc, msg, args): def send(self, irc, msg, args):
"""<recipient> <text> """<recipient> <text>
@ -135,12 +143,13 @@ class Note(callbacks.Privmsg):
public = 1 public = 1
else: else:
public = 0 public = 0
cursor = self.db.cursor() db = self.dbHandler.getDb()
cursor = db.cursor()
now = int(time.time()) now = int(time.time())
cursor.execute("""INSERT INTO notes VALUES cursor.execute("""INSERT INTO notes VALUES
(NULL, %s, %s, %s, 0, 0, %s, %s)""", (NULL, %s, %s, %s, 0, 0, %s, %s)""",
fromId, toId, now, public, note) fromId, toId, now, public, note)
self.db.commit() db.commit()
cursor.execute("""SELECT id FROM notes WHERE cursor.execute("""SELECT id FROM notes WHERE
from_id=%s AND to_id=%s AND added_at=%s""", from_id=%s AND to_id=%s AND added_at=%s""",
fromId, toId, now) fromId, toId, now)
@ -158,7 +167,8 @@ class Note(callbacks.Privmsg):
except KeyError: except KeyError:
irc.error(msg, conf.replyNotRegistered) irc.error(msg, conf.replyNotRegistered)
return return
cursor = self.db.cursor() db = self.dbHandler.getDb()
cursor = db.cursor()
cursor.execute("""SELECT note, to_id, from_id, added_at, public cursor.execute("""SELECT note, to_id, from_id, added_at, public
FROM notes FROM notes
WHERE (to_id=%s OR from_id=%s) AND id=%s""", WHERE (to_id=%s OR from_id=%s) AND id=%s""",
@ -202,7 +212,8 @@ class Note(callbacks.Privmsg):
except KeyError: except KeyError:
irc.error(msg, conf.replyNotRegistered) irc.error(msg, conf.replyNotRegistered)
return return
cursor = self.db.cursor() db = self.dbHandler.getDb()
cursor = db.cursor()
cursor.execute("""SELECT id, from_id, public cursor.execute("""SELECT id, from_id, public
FROM notes FROM notes
WHERE notes.to_id=%s AND notes.read=0""", id) WHERE notes.to_id=%s AND notes.read=0""", id)
@ -224,7 +235,8 @@ class Note(callbacks.Privmsg):
except KeyError: except KeyError:
irc.error(msg, conf.replyNotRegistered) irc.error(msg, conf.replyNotRegistered)
return return
cursor = self.db.cursor() db = self.dbHandler.getDb()
cursor = db.cursor()
cursor.execute("""SELECT id, from_id, public cursor.execute("""SELECT id, from_id, public
FROM notes FROM notes
WHERE notes.to_id=%s AND notes.read=1""", id) WHERE notes.to_id=%s AND notes.read=1""", id)

View File

@ -54,11 +54,49 @@ import ircutils
import privmsgs import privmsgs
import callbacks import callbacks
try:
import sqlite
Connection = sqlite.Connection
class MyConnection(sqlite.Connection):
def commit(self, *args, **kwargs):
if self.autocommit:
return
else:
Connection.commit(self, *args, **kwargs)
sqlite.Connection = MyConnection
except ImportError:
pass
class DBHandler(object):
def __init__(self, name=None, suffix='.db'):
if name is None:
self.name = self.__class__.__name__
else:
self.name = name
if suffix and suffix[0] != '.':
suffix = '.' + suffix
self.suffix = suffix
self.cachedDb = None
def makeFilename(self):
return self.name + self.suffix
def makeDb(self, filename):
raise NotImplementedError
def getDb(self):
if self.cachedDb is None or \
threading.currentThread() is not world.mainThread:
db = self.makeDb(self.makeFilename())
else:
db = self.cachedDb
db.autocommit = 1
return db
class ChannelDBHandler(object): class ChannelDBHandler(object):
"""A class to handle database stuff for individual channels transparently. """A class to handle database stuff for individual channels transparently.
""" """
suffix = '.db' suffix = '.db'
threaded = False
def __init__(self, suffix='.db'): def __init__(self, suffix='.db'):
self.dbCache = ircutils.IrcDict() self.dbCache = ircutils.IrcDict()
suffix = self.suffix suffix = self.suffix
@ -78,16 +116,13 @@ class ChannelDBHandler(object):
def getDb(self, channel): def getDb(self, channel):
"""Use this to get a database for a specific channel.""" """Use this to get a database for a specific channel."""
try: if channel not in self.dbCache or \
if self.threaded: threading.currentThread() is not world.mainThread:
return self.makeDb(self.makeFilename(channel))
else:
return self.dbCache[channel]
except KeyError:
db = self.makeDb(self.makeFilename(channel)) db = self.makeDb(self.makeFilename(channel))
if not self.threaded: else:
self.dbCache[channel] = db db = self.dbCache[channel]
return db db.autocommit = 1
return db
def die(self): def die(self):
for db in self.dbCache.itervalues(): for db in self.dbCache.itervalues():

View File

@ -41,16 +41,15 @@ import sre
import time import time
import types import types
import atexit import atexit
try: import threading
import msvcrt
except ImportError:
pass
import conf import conf
import debug import debug
startedAt = 0.0 startedAt = 0.0
mainThread = threading.currentThread()
threadsSpawned = 1 # Starts at one for the initial "thread." threadsSpawned = 1 # Starts at one for the initial "thread."
commandsProcessed = 0 commandsProcessed = 0
### ###
@ -75,7 +74,10 @@ def upkeep(): # Function to be run on occasion to do upkeep stuff.
collected = gc.collect() collected = gc.collect()
if os.name == 'nt': if os.name == 'nt':
try: try:
import msvcrt
msvcrt.heapmin() msvcrt.heapmin()
except ImportError:
pass
except IOError: # Win98 sux0rs! except IOError: # Win98 sux0rs!
pass pass
if gc.garbage: if gc.garbage: