#! /usr/bin/python
+### -*-python-*-
+###
+### Calculate discrete logs in groups
+###
+### (c) 2017 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of Rhodes, a distributed discrete-log finder.
+###
+### Rhodes is free software; you can redistribute it and/or modify
+### it under the terms of the GNU General Public License as published by
+### the Free Software Foundation; either version 2 of the License, or
+### (at your option) any later version.
+###
+### Rhodes is distributed in the hope that it will be useful,
+### but WITHOUT ANY WARRANTY; without even the implied warranty of
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+### GNU General Public License for more details.
+###
+### You should have received a copy of the GNU General Public License
+### along with Rhodes; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from sys import argv, stdout, stderr, exit
import errno as E
import catacomb as C
import sqlite3 as SQL
+###--------------------------------------------------------------------------
+### Miscellaneous utilities.
+
class ExpectedError (Exception):
pass
+###--------------------------------------------------------------------------
+### Database handling.
+
CONNINIT_SQL = """
PRAGMA foreign_keys = on;
"""
FOREIGN KEY (p, k) REFERENCES progress (p, k));
"""
+def connect_db(dir):
+ db = SQL.connect(OS.path.join(dir, 'db'))
+ db.text_factory = str
+ c = db.cursor()
+ c.executescript(CONNINIT_SQL)
+ return db
+
+###--------------------------------------------------------------------------
+### Group support.
+
GROUPMAP = {}
class GroupClass (type):
def getgroup(kind, desc): return GROUPMAP[kind](desc)
+###--------------------------------------------------------------------------
+### Number-theoretic utilities.
+
def factor(n):
ff = []
proc = S.Popen(['./factor', str(n)], stdout = S.PIPE)
if rc: raise ExpectedError, 'factor failed: rc = %d' % rc
return ff
+###--------------------------------------------------------------------------
+### Command dispatch.
+
CMDMAP = {}
def defcommand(f, name = None):
CMDMAP[name] = f
return f
-def connect_db(dir):
- db = SQL.connect(OS.path.join(dir, 'db'))
- db.text_factory = str
+###--------------------------------------------------------------------------
+### Job status utilities.
+
+def get_top(db):
c = db.cursor()
- c.executescript(CONNINIT_SQL)
- return db
+ c.execute("""SELECT kind, groupdesc, g, x, m, n FROM top""")
+ kind, groupdesc, gstr, xstr, mstr, nstr = c.fetchone()
+ G = getgroup(kind, groupdesc)
+ g, x, m = G.elt(gstr), G.elt(xstr), C.MP(mstr)
+ n = nstr is not None and C.MP(nstr) or None
+ return G, g, x, m, n
+
+def get_job(db):
+ c = db.cursor()
+ c.execute("""SELECT p.p, p.e, p.k, p.n, p.dpbits
+ FROM progress AS p LEFT OUTER JOIN workers AS w
+ ON p.p = w.p and p.k = w.k
+ WHERE p.k < p.e AND (p.dpbits > 0 OR w.pid IS NULL)
+ LIMIT 1""")
+ row = c.fetchone()
+ if row is None: return None, None, None, None, None
+ else:
+ pstr, e, k, nstr, dpbits = row
+ p, n = C.MP(pstr), C.MP(nstr)
+ return p, e, k, n, dpbits
+
+def maybe_cleanup_worker(dir, db, pid):
+ c = db.cursor()
+ f = OS.path.join(dir, 'lk.%d' % pid)
+ state = 'LIVE'
+ try: fd = OS.open(f, OS.O_WRONLY)
+ except OSError, err:
+ if err.errno != E.ENOENT: raise ExpectedError, 'open lockfile: %s' % err
+ state = 'STALE'
+ else:
+ try: F.lockf(fd, F.LOCK_EX | F.LOCK_NB)
+ except IOError, err:
+ if err.errno != E.EAGAIN: raise ExpectedError, 'check lock: %s' % err
+ else:
+ state = 'STALE'
+ if state == 'STALE':
+ try: OS.unlink(f)
+ except OSError: pass
+ c.execute("""DELETE FROM workers WHERE pid = ?""", (pid,))
+
+def maybe_kill_worker(dir, pid):
+ f = OS.path.join(dir, 'lk.%d' % pid)
+ try: fd = OS.open(f, OS.O_RDWR)
+ except OSError, err:
+ if err.errno != E.ENOENT: raise ExpectedError, 'open lockfile: %s' % err
+ return
+ try: F.lockf(fd, F.LOCK_EX | F.LOCK_NB)
+ except IOError, err:
+ if err.errno != E.EAGAIN: raise ExpectedError, 'check lock: %s' % err
+ else: return
+ OS.kill(pid, SIG.SIGTERM)
+ try: OS.unlink(f)
+ except OSError: pass
+
+###--------------------------------------------------------------------------
+### Setup.
@defcommand
def setup(dir, kind, groupdesc, gstr, xstr):
c.execute("""INSERT INTO progress (p, e, dpbits) VALUES (?, ?, ?)""",
(str(p), e, dpbits))
-def get_top(db):
- c = db.cursor()
- c.execute("""SELECT kind, groupdesc, g, x, m, n FROM top""")
- kind, groupdesc, gstr, xstr, mstr, nstr = c.fetchone()
- G = getgroup(kind, groupdesc)
- g, x, m = G.elt(gstr), G.elt(xstr), C.MP(mstr)
- n = nstr is not None and C.MP(nstr) or None
- return G, g, x, m, n
+###--------------------------------------------------------------------------
+### Check.
@defcommand
def check(dir):
exit(rc[0])
-def get_job(db):
- c = db.cursor()
- c.execute("""SELECT p.p, p.e, p.k, p.n, p.dpbits
- FROM progress AS p LEFT OUTER JOIN workers AS w
- ON p.p = w.p and p.k = w.k
- WHERE p.k < p.e AND (p.dpbits > 0 OR w.pid IS NULL)
- LIMIT 1""")
- row = c.fetchone()
- if row is None: return None, None, None, None, None
- else:
- pstr, e, k, nstr, dpbits = row
- p, n = C.MP(pstr), C.MP(nstr)
- return p, e, k, n, dpbits
+###--------------------------------------------------------------------------
+### Done.
@defcommand
def done(dir):
if p is None: exit(2)
else: exit(1)
-def maybe_cleanup_worker(dir, db, pid):
- c = db.cursor()
- f = OS.path.join(dir, 'lk.%d' % pid)
- state = 'LIVE'
- try: fd = OS.open(f, OS.O_WRONLY)
- except OSError, err:
- if err.errno != E.ENOENT: raise ExpectedError, 'open lockfile: %s' % err
- state = 'STALE'
- else:
- try: F.lockf(fd, F.LOCK_EX | F.LOCK_NB)
- except IOError, err:
- if err.errno != E.EAGAIN: raise ExpectedError, 'check lock: %s' % err
- else:
- state = 'STALE'
- if state == 'STALE':
- try: OS.unlink(f)
- except OSError: pass
- c.execute("""DELETE FROM workers WHERE pid = ?""", (pid,))
-
-def maybe_kill_worker(dir, pid):
- f = OS.path.join(dir, 'lk.%d' % pid)
- try: fd = OS.open(f, OS.O_RDWR)
- except OSError, err:
- if err.errno != E.ENOENT: raise ExpectedError, 'open lockfile: %s' % err
- return
- try: F.lockf(fd, F.LOCK_EX | F.LOCK_NB)
- except IOError, err:
- if err.errno != E.EAGAIN: raise ExpectedError, 'check lock: %s' % err
- else: return
- OS.kill(pid, SIG.SIGTERM)
- try: OS.unlink(f)
- except OSError: pass
+###--------------------------------------------------------------------------
+### Step.
@defcommand
def step(dir, cmd, *args):
with db:
c.execute("""DELETE FROM workers WHERE pid = ?""", (mypid,))
+###--------------------------------------------------------------------------
+### Top-level program.
+
PROG = argv[0]
try: