def __call__(self, cv_iterator, evaluator, fold_callback=None, n_jobs=None): """ """ condvar = Condition() results = [] def _signal_cb(result): condvar.acquire() results.append(result) condvar.notify() condvar.release() folds = list(cv_iterator) pool, deferreds = self.async(folds, evaluator, fold_callback=_signal_cb, n_jobs=n_jobs) pool.close() while len(results) < len(folds): condvar.acquire() condvar.wait() fold_estimator, result = results[-1] fold_callback(fold_estimator, result) condvar.release() pool.join() return results
def getDataSet(time0, users, rgp): start = time.time() plt_queue = Queue() plt_cond = Condition() pool = [] max_process = 2 i = 0 n_user = len(users.index) while i < n_user: if len(pool) >= max_process: plt_cond.acquire() if plt_queue.empty(): plt_cond.wait() while not plt_queue.empty(): pos = -1 uq = plt_queue.get() for pos in range(len(pool)): if pool[pos].u == uq: break pool[pos].join() del pool[pos] plt_cond.release() u = users.index[i] m = users.loc[u, "memberSince"] p = MultipRecord(rgp, u, m, time0, plt_queue, plt_cond) pool.append(p) p.start() i += 1 print "subProcess start....." [p.join() for p in pool] end = time.time() print "time for make Reg trainSet: %.3f s" % (end - start)
class Producer(Process): def __init__(self, prod_end, fname, SHARED_QUEUE_SIZE_LIMIT): super(Producer, self).__init__() self.prod_end = prod_end self.fp = open(fname, 'r') self.SHARED_QUEUE_SIZE_LIMIT = SHARED_QUEUE_SIZE_LIMIT self.batch_queue = [] self.condition = Condition() self.pipe_out_thread = PipeOutThread(prod_end, self.condition, self.SHARED_QUEUE_SIZE_LIMIT, self.batch_queue) def _preprocess(self, data): N = 1000 * 1000 * 10 * 3 while N > 0: N -= 1 return data def _is_shared_queue_full(self): if self.pipe_out_thread.get_queue_size( ) >= self.SHARED_QUEUE_SIZE_LIMIT: return True else: return False def _preprocess_and_put_in_queue(self, data): self.condition.acquire() #print 'prod acquired' if self._is_shared_queue_full(): #print 'prod: queue is full so waiting' self.condition.wait() self.batch_queue.append(self._preprocess(data)) #print 'self.batch_queue', len(self.batch_queue) self.condition.notify() self.condition.release() def _read_data(self, i, dummy=False): if dummy: return 'soumya' else: data = None offset = random.randint(5, 16) * GB #print 'offset is : ' , offset/GB self.fp.seek(offset) data = self.fp.read(CHUNK_SIZE_TO_READ) #print 'len_data ', len(data) return data def run(self): self.pipe_out_thread.start() for i in range(BATCHES): data = self._read_data(i) self._preprocess_and_put_in_queue(data) #print 'prod put %s'%(i) self.pipe_out_thread.join()
def test_watch_directory(): def _cleanup(path): for f in listdir(path): p = join(path, f) if isdir(p): rmtree(p) elif f != '.nothing': unlink(p) sample_template = '' sample_directory = dirname(realpath(__file__)) + '/sample/' watch_directory = sample_directory + 'watch/' render_directory = sample_directory + 'render/' template_directory = sample_directory + 'templates/' with open(template_directory + 'haml.tmpl', 'r') as f: sample_template = f.read() condition = Condition() p = Process(target=reloader.watch_directory, args=(watch_directory, render_directory, condition)) condition.acquire() p.start() condition.wait() try: with open(watch_directory + 'test.haml', 'w') as f: f.write(sample_template) subdir = watch_directory + 'test_subdir/' try: mkdir(subdir) except OSError: if not isdir(subdir): raise with open(subdir + 'test_two.haml', 'w') as f: f.write(sample_template) sleep(1) assert_true(exists(render_directory + 'test.html')) assert_true(exists(render_directory + 'test_subdir/test_two.html')) except: raise finally: condition.release() p.terminate() p.join() sleep(1) _cleanup(watch_directory) _cleanup(render_directory)
class SynchronizingBus(Bus): def __init__(self, sync_delay=1): Bus.__init__(self) self.sync_delay = sync_delay self.condition = Condition() def start(self): import time time.sleep(self.sync_delay) self.log("Releasing children") self.condition.acquire() self.condition.notify_all() self.condition.release() Bus.start(self)
def genSimTask(path, days=0, thd=0.8): start = time.time() pool = [] selTasks = pd.read_pickle(path + "challenges.data") selTasks = selTasks.iloc[:100, :] taskIndex = selTasks.index maxProcess = 64 sel_queue = Queue() sel_cond = Condition() for i in range(len(taskIndex)): if len(pool) >= maxProcess: sel_cond.acquire() if sel_queue.empty(): sel_cond.wait() while not sel_queue.empty(): pos = -1 name0 = sel_queue.get() for pos in range(len(pool)): if pool[pos].task0.name == name0: break pool[pos].join() del pool[pos] sel_cond.release() task0 = selTasks.iloc[i] cand_tasks = selTasks[selTasks["RegStart"] <= task0["SubEnd"]] if days > 0: cand_tasks = cand_tasks[task0["RegStart"] - cand_tasks["RegStart"] <= days] filepath0 = path + "simTasks%d/sim_tasks_%d.data" % (days, taskIndex[i]) proc = SimTasks(filepath0, task0, cand_tasks, thd, sel_queue, sel_cond) proc.start() pool.append(proc) [pro.join() for pro in pool] end = time.time() print "time for generate sim_task %.3f s." % (end - start) simTaskContainer = {} for taskI in taskIndex: filepath0 = path + "simTasks%d/sim_tasks_%d.data" % (days, taskIndex[i]) with open(filepath0, "rb") as f: sim_tasks = pickle.load(f) simTaskContainer[taskI] = sim_tasks sum_path = path + "simTasks/sim_tasks_%d.data" % days with open(sum_path, "wb") as f: pickle.dump(simTaskContainer, f)
class QueuedDistributor(BaseDistributor): def __init__(self, socket_handler): BaseDistributor.__init__(self, socket_handler) self.socket_queue = SimpleQueue() self.queue_condition = Condition(Lock()) def new_worker(self): return Process(target=QueuedWorker, args=(self.socket_handler, self.socket_queue, self.queue_condition)) def process_handel(self, socket_handel): self.queue_condition.acquire() self.socket_queue.put(socket_handel) self.queue_condition.notify() self.queue_condition.release()
class CountDownLatch(object): """A synchronization aid that allows one or more processes to wait until a set of operations being performed in other processes completes. A CountDownLatch is initialized with a given count. The wait method blocks until the current count reaches zero due to invocations of the count_down() method, after which all waiting processes are released and any subsequent invocations of wait return immediately. The count can be reset, but one need to be 100% sure no other processes are waiting or counting down during reset. """ def __init__(self, count: int = 1, lock=None): self.__count = Value('i', count, lock=True if lock is None else lock) self.__lock = Condition(lock) def reset(self, count): self.__lock.acquire() self.__count.value = count self.__lock.release() def count_down(self): self.__lock.acquire() self.__count.value -= 1 result = self.__count.value if self.__count.value <= 0: self.__lock.notify_all() self.__lock.release() return result def wait(self, timeout=None): self.__lock.acquire() result = self.__lock.wait_for(lambda: self.__count.value <= 0, timeout) self.__lock.release() return result
class RWLock: def __init__(self): self.cond = Condition() self.readers = 0 def read_acquire(self): self.cond.acquire() self.readers += 1 self.cond.release() def read_release(self): with self.cond: self.readers -= 1 if (self.readers == 0): self.cond.notify_all() def write_acquire(self): self.cond.acquire() if (self.readers > 0): self.cond.wait() def write_release(self): self.cond.release()
class BARRIER: def __init__(self, n): self.n = n self.i = Value('i', 0) self.lock = Lock() self.condition = Condition() def acquire(self): with self.lock: self.i.value += 1 if self.n == self.i.value: self.i.value = 0 return True #print self.i.value,self.n self.condition.acquire() self.condition.wait() self.condition.release() return False def release(self): self.condition.acquire() self.condition.notify_all() self.condition.release()
class kernel: """ Guarda la estructura con la imformacion referente al listener """ def __init__(self,dim_i=3,dim_j=1): self.jugadores=[] self.M=Manager() self.listenner=Listener(address=('localhost',6000),authkey='secret password') self.color=0 self.numCasillas=0 self.numEspera=self.M.list([0,0,self.numCasillas]) self.desocupado=Condition() self.cont=0 self.turno=self.M.list([0]) self.dim_i=dim_i self.dim_j=dim_j self.len=self.dim_i*self.dim_j self.tablero=(self.M.list([0]*(self.dim_i*self.dim_j))) def trhows(self): ''' lanza los programas iniciando el listenner accion carga_nuevos_jugadores ''' CNP=Process(target=self.carga_nuevos_jugadores) CNP.start();CNP.join() def carga_nuevos_jugadores(self): print 'carga up' while self.numCasillas<self.len: new_con=self.listenner.accept() self.jugadores.append(new_con) new_con.send((self.color,(self.dim_i,self.dim_j))) P=Process(target=self.check,args=(new_con,self.color)) (self.color,self.cont)=(1-self.color,self.cont+1) print 'pass' P.start() print 'pass2' def check(self,con,color): ''' Controla el I/O de un jugador print 'check up' ''' cont=True while cont and self.numCasillas<self.len: print 'esperando para colocar' recibido=print_(con.recv()) if recibido!='q': cont2=True while cont2: if self.desocupado.acquire(): R=self.protocol(recibido) self.desocupado.notify_all() self.desocupado.release() con.send(R) if R[0]==2: sleep(1.0) con.send(R) cont2=False else: self.numEspera[color]+=1 print 'pacientes:', self.numEspera self.desocupado.notify_all() self.desocupado.release() self.desocupado.wait() self.numEspera[color]-=1 else: con.close() cont=False def protocol(self,recibido): if (recibido[0]==self.turno[0]) or (self.numEspera[1-self.turno[0]]==0): #self.desocupado.acquire(): if (self.tablero[(recibido[1])])==0: self.turno[0]=1-recibido[0] self.tablero[(recibido[1])]=recibido[0]+1 self.numEspera[2]+=1 Ans=(1,self.dim_i,self.dim_j,self.tablero[:]) else: Ans=(2,self.dim_i,self.dim_j,self.tablero[:]) else: Ans= (0,self.dim_i,self.dim_j,self.tablero[:]) if self.numEspera[2]==len(self.tablero): Ans=(3,self.dim_i,self.dim_j,self.tablero[:]) return Ans
class MultiProcessScheduler: def __init__(self): self.cond = Condition() # default to RLock # If duplex is False then the pipe is unidirectional # conn1 for receiving messages and conn2 for sending messages. conn1, conn2 = Pipe(duplex=False) self.connREAD = conn1 self.connWRITE = conn2 # a holder to the closest task to execute # it is not safe to access this variable directly as # there might be data on the pipe, use self.__getClosestTask() self._closestTask = None # multiprocessing.Queue is used here to exchange task between the add # call and the service running __run() method self.queue = SimpleQueue() # dummy Process, the correct one will be created when the first # task is added self.service = Process() # TODO create destructor to avoid leaving with items on queue def __getClosestTask(self): ''' return the closest task to execute (i.e., top on pq) ''' if self.connREAD.poll(): ret = None while self.connREAD.poll(): ret = self.connREAD.recv() self._closestTask = ret print("[conn] closestTaskUpdate: ", self._closestTask) return self._closestTask def add(self, task): if type(task) is not Task: raise TypeError self.queue.put(task) if not self.service.is_alive(): # it seams that Process.run() is a blocking call # so the only way to re-run the process is to create another one self.service = Process(target=MultiProcessScheduler.__run, args=(self.cond, self.queue, self.connWRITE), daemon=False) self.service.start() else: # notify the condition variable if the new task has the # closest execution time closestTask = self.__getClosestTask() if closestTask and task.time < closestTask.time: self.cond.acquire() self.cond.notify() self.cond.release() @staticmethod def __run(cond, queue, conn): tasksQueue = [] print("[run] starting", queue.empty()) while True: # remove tasks from queue and add to # internal priorityQueue (tasksQueue) while not queue.empty(): task = queue.get() heappush(tasksQueue, task) print("[run] adding task to pq: ", task) # if there are task on the priority queue, # check when the closest one should be runned if tasksQueue: etime, _, _ = task = tasksQueue[0] now = time() if etime < now: # only pop before running # if a task is not being running in a given time, # the next this loop runs that task might not be the # closest one _, fn, args = heappop(tasksQueue) print("[run] running:", task) p = Process(target=fn, args=args, daemon=False) p.start() else: delay = etime - now print("[run] sleeping for ", delay, task) # send the closest task to the pipe conn.send(task) cond.acquire() cond.wait(timeout=delay) if not tasksQueue and queue.empty(): # only stop the service if there are no task anwhere break print("[run] done")
class TProcessPoolServer(TServer): def __init__(self, *args): TServer.__init__(self, *args) self.numWorkers = 10 self.workers = [] self.isRunning = Value('b', False) self.stopCondition = Condition() self.postForkCallback = None def setPostForkCallback(self, callback): if not callable(callback): raise TypeError("This is not a callback!") self.postForkCallback = callback def setNumWorkers(self, num): """Set the number of worker threads that should be created""" self.numWorkers = num def workerProcess(self): """Loop getting clients from the shared queue and process them""" if self.postForkCallback: self.postForkCallback() while self.isRunning.value: try: client = self.serverTransport.accept() if not client: continue self.serveClient(client) except (KeyboardInterrupt, SystemExit): return 0 except Exception as x: logger.exception(x) def serveClient(self, client): itrans = self.inputTransportFactory.getTransport(client) otrans = self.outputTransportFactory.getTransport(client) iprot = self.inputProtocolFactory.getProtocol(itrans) oprot = self.outputProtocolFactory.getProtocol(otrans) try: while True: self.processor.process(iprot, oprot) except TTransportException: pass except Exception as x: logger.exception(x) itrans.close() otrans.close() def serve(self): self.isRunning.value = True self.serverTransport.listen() for i in range(self.numWorkers): try: w = Process(target=self.workerProcess) w.daemon = True w.start() self.workers.append(w) except Exception as x: logger.exception(x) while True: self.stopCondition.acquire() try: self.stopCondition.wait() break except (SystemExit, KeyboardInterrupt): break except Exception as x: logger.exception(x) self.isRunning.value = False def stop(self): self.isRunning.value = False self.stopCondition.acquire() self.stopCondition.notify() self.stopCondition.release()
for j in range(len(pool_processes)): p = pool_processes[j] if p.taskid == taskid: rmPs.append(j) #print(pool_processes[j].taskid,"finished") break rmPs.sort() rmPs.reverse() #print(rmPs) for j in rmPs: #pool_processes[j].join() del pool_processes[j] finishSig.release() while len(pool_processes) > 0: rmPs = [] while queue.empty() == False: result = queue.get() user_m = result[1].toarray() taskid = result[0] data = {} data["users"] = users data["data"] = user_m ranks, names = rankOnDIG(data) data = {"users": names, "ranks": ranks} #print("fetched",taskid) dataGraph[taskid] = data
class CartPoleEvaluation: def __init__(self, k: int): self.returns = [] self.curTrialReturns = [] self.numTrial = 0 self.k = k self.lock = Condition() # print("New Cartpole Eval") def endTrial(self): print("Incrementing Num Trial", self.numTrial) self.numTrial += 1 self.returns.append(np.array(self.curTrialReturns)) self.curTrialReturns = [] # self.lock.acquire() # self.lock.release() def __call__(self, parameters: np.array, numEpisodes: int): # print("Evaluating Cartpole") G = [] # self.policy.parameters = policy policy = SoftmaxWithLFA(4, 2, self.k) policy.parameters = parameters # threadPool = Pool(numEpisodes) # G = threadPool.map(self.runEpisode, [policy for i in range(numEpisodes)]) # print("G", G) # self.curTrialReturns.extend(G) env = Cartpole() for ep in range(numEpisodes): # print("Episode ", ep) env.reset() Gi = 0 while not env.isEnd: state = env.state action = policy.samplAction(state) next_state, reward, _ = env.step(action) Gi += reward G.append(Gi) self.lock.acquire() self.curTrialReturns.append(Gi) # print("Number of returns collected int trial", len(self.curTrialReturns)) self.lock.release() # threadPool.close() # threadPool.join() print("Mean Return ", np.mean(G)) return np.mean(G) def runEpisode(self, policy): env = Cartpole() Gi = 0 while not env.isEnd: state = env.state action = policy.samplAction(state) next_state, reward, _ = env.step(action) Gi += reward return Gi def plot(self, filename: str = "plot.png", title: str = "plot", show: bool = False): fig, ax = plt.subplots() numEpisodes = len(self.returns[0]) # print (self.numTrial, numEpisodes) returns = np.reshape(self.returns, (self.numTrial, numEpisodes)) print("Plotting Returns of shape ", returns.shape) ax.errorbar(np.arange(numEpisodes), np.mean(returns, axis=0), yerr=np.std(returns, axis=0), ecolor='gray', label='1 standard deviation') # plt.yscale('log') ax.set_xlabel('Num Episodes') ax.set_ylabel('Return') # ax.xaxis.set_major_locator(MultipleLocator(500)) ax.xaxis.set_minor_locator(AutoMinorLocator(5)) # ax.yaxis.set_major_locator(MultipleLocator(5)) ax.yaxis.set_minor_locator(AutoMinorLocator(5)) ax.grid(True, which='major', linestyle='-', linewidth='0.5', color='black') ax.grid(True, which='minor', linestyle=':', linewidth='0.2', color='red') ax.legend(loc=4) if show: ax.show() fig.suptitle(title, fontsize=10) fig.savefig(filename)
class SharedStorage(object): """ Uložiště synchronizované mezi procesy. """ class StorageType(Enum): LIST = "list" #SharedStorageList - wrapped manager.list() DICT = "DICT" #SharedStorageDict - wrapped manager.dict() DICT_SIMPLE = "DICT_SIMPLE" #manager.dict() Pokud nepotřebujeme pracovat s velkým objemem dat (nad 2GB), tak je vhodnější. def __init__(self, storageType, manager=None): """ Inicializace uložiště. :type storageType: StorageType :param storageType: Druh uložiště. Všechny podporované druhy vymezuje StorageType. :param manager: Volitelný parametr. Pokud chceme vnutit použití jiného multiprocessing.Manager. """ if manager is None: manager = Manager() # Type checking if not isinstance(storageType, self.StorageType): raise TypeError('storageType musí být instancí StorageType') #Zde budou ukládány data, if storageType == self.StorageType.LIST: self._storage = SharedList(manager) elif storageType == self.StorageType.DICT: self._storage = SharedDict(manager) elif storageType == self.StorageType.DICT_SIMPLE: self._storage = manager.dict() else: raise ValueError('Neznámý druh uložiště (storageType).') self.__usedManager = manager #Sdílený zámek pro synchronizaci procesů self.__sharedLock = Lock() #počet uložených klasifikátorů self._numOfData = Value(c_ulong, 0) self.__waitForChange = Condition() self.acquiredStorage = False def __len__(self): """ Zjištení počtu uložených dat. :return: Počet všech uložených dat. :rtype: int """ return self._numOfData.value def _notifyChange(self): """ Oznámí, že došlo ke změně připadným čekajícím. """ self.__waitForChange.acquire() self.__waitForChange.notify_all() self.__waitForChange.release() def waitForChange(self, timeout=None): """ Uspí proces dokud nenastane změna. Pokud měl proces přivlastněné uložiště, tak je uvolní a po probuzení zase přivlastní. :param timeout: Maximální počet sekund, které bude čekat. Může být None, pak čeká dokud nepřijde událost. """ wasAcquiredBeforeSleeping = False if self.acquiredStorage: self.release() wasAcquiredBeforeSleeping = True self.__waitForChange.acquire() self.__waitForChange.wait(timeout) self.__waitForChange.release() if wasAcquiredBeforeSleeping: self.acquire() def acquire(self): """ Přivlastní si uložiště pro sebe. Ostatní procesy musí čekat. """ self.__sharedLock.acquire() self.acquiredStorage = True def release(self): """ Uvolní uložiště pro ostatní procesy. """ self.__sharedLock.release() self.acquiredStorage = False def _safeAcquire(self): """ Přivlastnění si uložiště. V momentu, kdy chci měnit jeho stav. Zohledňuje případ, kdy je uložiště zamluveno pomocí acquire. """ if not self.acquiredStorage: self.__sharedLock.acquire() def _safeRelease(self): """ Uvolnění přístupu k uložišti. Zohledňuje případ, kdy je uložiště zamluveno pomocí acquire. """ if not self.acquiredStorage: self.__sharedLock.release()
from config import TASK_QUEUE_SIZE, UPDATE_TIME if __name__ == "__main__": myip = getMyIP() DB_PROXY_NUM = Value('i', 0) q1 = Queue(maxsize=TASK_QUEUE_SIZE) q2 = Queue() sleep_condition = Condition(Lock()) p0 = Process(target=start_api_server, args=(sleep_condition, )) p1 = Process(target=startProxyCrawl, args=(q1, DB_PROXY_NUM, myip, sleep_condition)) p2 = Process(target=validator, args=(q1, q2, myip)) p3 = Process(target=store_data, args=(q2, DB_PROXY_NUM)) p0.start() p1.start() p2.start() p3.start() while True: now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f") print("sleep start:" + now) time.sleep(UPDATE_TIME) now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f") print("sleep finish:" + now) sleep_condition.acquire() sleep_condition.notify() sleep_condition.release() p0.join() p1.join() p2.join() p3.join()
class RWLock(): """A Readers-Writer lock. Allows for multiple readers or one writer. Writers will not starve. Attributes: for_reading (RWLock.ReadLock): A lock-like object with appropriate `acquire`, `release`, `__enter__` and `__exit__` methods pointed to the *read methods of the RWLock. Chiefly for use with the `with` statement. for_writing (RWLock.WriteLock): A lock-like object with appropriate `acquire`, `release`, `__enter__` and `__exit__` methods pointed to the *write methods of the RWLock. Chiefly for use with the `with` statement. """ class ReadLock(): def __init__(self, rw): self._rw = rw self.acquire = rw.acquire_read self.release = rw.release_read def __enter__(self): self.acquire() def __exit__(self, exception_type, exception_value, traceback): self.release() class WriteLock(): def __init__(self, rw): self._rw = rw self.acquire = rw.acquire_write self.release = rw.release_write def __enter__(self): self.acquire() def __exit__(self, exception_type, exception_value, traceback): self.release() def __init__(self): """Initialises the RWLock.""" self._condition = Condition() self._readers = Value(c_uint64, 0, lock=False) self._writers_waiting = Value(c_uint64, 0, lock=False) self.for_reading = self.ReadLock(self) self.for_writing = self.WriteLock(self) def acquire_read(self): """Acquire a read lock. Blocks if a thread has acquired the write lock or is waiting to acquire the write lock. """ with self._condition: while self._writers_waiting.value: self._condition.wait() self._readers.value += 1 def release_read(self): """Release a read lock.""" with self._condition: self._readers.value -= 1 if not self._readers.value: self._condition.notify_all() def acquire_write(self): """Acquire a write lock. Blocks until there are no acquired read or write locks. """ self._condition.acquire() self._writers_waiting.value += 1 while self._readers.value: self._condition.wait() self._writers_waiting.value -= 1 def release_write(self): """Release a write lock.""" self._condition.release()
class TModelPoolServer(TServer): ''' A server runs a pool of multiple models to serve requests Written by CongVm ''' def __init__(self, handler, listModelConfig, *args, logger=None, timeout=0.1, batchSize=1): TServer.__init__(self, *args) self.timeout = timeout self.batchSize = batchSize if logger is not None: self.logger = logger else: self.logger = logging.getLogger(__name__) self.listModelConfig = listModelConfig self.handler = handler self.workers = [] self.isRunning = Value('b', False) self.stopCondition = Condition() self.postForkCallback = None def setPostForkCallback(self, callback): if not callable(callback): raise TypeError("This is not a callback!") self.postForkCallback = callback def setListModelConfig(self, listModelConfig): """Set the number of worker threads that should be created""" self.listModelConfig = listModelConfig def workerProcess(self, kwargs): """Loop getting clients from the shared queue and process them""" # Init Processor here self.handlerInstance = self.handler(**kwargs) self.procInstance = self.processor(self.handlerInstance) if self.postForkCallback: self.postForkCallback() listClient = [] t = time() while self.isRunning.value: try: client = self.serverTransport.accept() if not client: continue listClient.append(client) if len(listClient) >= self.batchSize or time() - t >= self.timeout: self.serveClient(self.procInstance, listClient) listClient.clear() t = time() except (KeyboardInterrupt, SystemExit): return 0 except Exception as x: self.logger.exception(x) def parseClients(self, listClient): listOtrans = [] listItrans = [] listIprot = [] listOprot = [] for client in listClient: itrans = self.inputTransportFactory.getTransport(client) otrans = self.outputTransportFactory.getTransport(client) iprot = self.inputProtocolFactory.getProtocol(itrans) oprot = self.outputProtocolFactory.getProtocol(otrans) listOtrans.append(otrans) listItrans.append(itrans) listIprot.append(iprot) listOprot.append(oprot) return listOtrans, listItrans, listIprot, listOprot def serveClient(self, procInstance, listClient): """Process input/output from a client for as long as possible""" listOtrans, listItrans, listIprot, listOprot = self.parseClients(listClient) try: while True: procInstance.process(listIprot, listOprot) except TTransportException: pass except Exception as x: self.logger.exception(x) for itrans, otrans in zip(listItrans, listOtrans): itrans.close() otrans.close() def serve(self): """Start workers and put into queue""" # this is a shared state that can tell the workers to exit when False self.isRunning.value = True # first bind and listen to the port self.serverTransport.listen() # fork the children for modelConfig in self.listModelConfig: try: w = Process(target=self.workerProcess, args=(modelConfig, )) w.daemon = True w.start() self.workers.append(w) except Exception as x: self.logger.exception(x) # wait until the condition is set by stop() while True: self.stopCondition.acquire() try: self.stopCondition.wait() break except (SystemExit, KeyboardInterrupt): break except Exception as x: self.logger.exception(x) self.isRunning.value = False def stop(self): self.isRunning.value = False self.stopCondition.acquire() self.stopCondition.notify() self.stopCondition.release()
class StateLatch(object): """A synchronization aid that allows one or more processes to wait for state change until a set of operations being performed in other processes completes. A StateLatch is initialized with a given state. The wait and wait_for methods block until the current state changes to the desired state due to invocations of the next() method, after which all waiting processes are released and any subsequent invocations of wait or wait_for return immediately. While changing state one can set the counter of the next state. The next state won't take effect due to invocations of the next() method until the counter reaches zero. This ensures the requested number of processes completed their work when state actually changes. The counter of next state can be amended without state change using set_next method, but one need to be 100% sure no other processes are waiting or changing state at the moment. """ def __init__(self, state=State.READY, lock: RLock = None): self.__state = Value('i', state, lock=True if lock is None else lock) self.__lock = Condition(lock) self.__next_state_count_down = CountDownLatch(0, lock) self.__next_state_count_down_max = Value( 'i', 0, lock=True if lock is None else lock) def set_next(self, next_state_count_down): self.__lock.acquire() self.__next_state_count_down.reset(next_state_count_down) self.__next_state_count_down_max.value = 0 self.__lock.release() def next(self, next_state_count_down: int = 0): self.__lock.acquire() old = State(self.__state.value) self.__next_state_count_down_max.value = max( self.__next_state_count_down_max.value, next_state_count_down) if self.__next_state_count_down.wait(0) or \ self.__next_state_count_down.count_down() == 0: self.__state.value = State.next(self.__state.value) self.__next_state_count_down.reset( self.__next_state_count_down_max.value) self.__next_state_count_down_max.value = 0 new = State(self.__state.value) self.__lock.notify_all() self.__lock.release() return old, new def wait(self, state, timeout=None): self.__lock.acquire() result = self.__lock.wait_for(lambda: self.__state.value == state, timeout) self.__lock.release() return result def wait_for(self, state, predicate, timeout=None): """Wait for the desired state or until a condition evaluates to true. predicate must be a callable with the result interpreted as a boolean value. A timeout may be provided giving the maximum time to wait. While waiting the predicate is being checked every second. :param state: state to wait for :param predicate: callable function with the result interpreted as a boolean value :param timeout: the maximum time to wait :return: the last return value of the predicate or False if the method timed out """ self.__lock.acquire() try: result = self.__state.value == state or predicate() if result: return result end_time = None if timeout is None else monotonic() + timeout wait_time = 1 while not result: if end_time is not None: wait_time = min(end_time - monotonic(), 1) if wait_time <= 0: break result = self.__lock.wait_for( lambda: self.__state.value == state, wait_time) or predicate() return result finally: self.__lock.release() @property def state(self): return State(self.__state.value)
class WaitableQueue(Queue): """Queue that uses a semaphore to reliably count items in it""" class Vacuum(ThreadLoop): def __init__(self, q, l): def callback(): q.wait_notempty(0.1) while True: try: val = q.get(False) l.append(val) except Empty: break ThreadLoop.__init__(self, callback) def __init__(self, maxsize=0): self.cond_empty = Condition() self.cond_notempty = Condition() self._put_counter = Value('i', 0) Queue.__init__(self, maxsize) def put(self, obj, block=True, timeout=None): Queue.put(self, obj, block, timeout) self._put_counter.value += 1 if self.qsize() != 0: self.cond_notempty.acquire() try: self.cond_notempty.notify_all() finally: self.cond_notempty.release() @property def put_counter(self): return self._put_counter.value def get(self, block=True, timeout=None): ret = Queue.get(self, block, timeout) if self.qsize() == 0: self.cond_empty.acquire() try: self.cond_empty.notify_all() finally: self.cond_empty.release() return ret def wait_empty(self, timeout=None): """Wait for all items to be got""" self.cond_empty.acquire() try: if self.qsize(): self.cond_empty.wait(timeout) finally: self.cond_empty.release() def wait_notempty(self, timeout=None): """Wait for all items to be got""" self.cond_notempty.acquire() try: if self.qsize() == 0: self.cond_notempty.wait(timeout) finally: self.cond_notempty.release()
import time from multiprocessing import Process, Condition import os cond = Condition() def wait_condition(): cond.acquire() cond.wait() print '[%d] waked!' % os.getpid() cond.release() for i in range(3): Process(target=wait_condition).start() time.sleep(1) print 'notify!' cond.acquire() cond.notify_all() cond.release()
class Game: def __init__(self, teams=None, point_limit=1500, use_counting_factor=False, seed=None): self.teams = teams self.point_limit = point_limit self.players = [ teams[0].players[0], teams[1].players[0], teams[0].players[1], teams[1].players[1] ] self.dealer = Dealer(players=self.players) self.geschoben = False self.trumpf = None self.stiche = [] self.cards_on_table = [] self.use_counting_factor = use_counting_factor self.seed = seed self.endless_play_control = Condition( ) # used to control the termination of the play_endless method self.stop_playing = False # has to be set to true in order to stop the endless play def play_endless(self, start_player_index=0, whole_rounds=True): """ Plays one game after the other with no end. This can be used for training a RL Player. Like this we can reuse one game. When we are training with tournaments, each time we play a game, it is added to the list of games. This could result in very high unneeded memory usage. :param start_player_index: :param whole_rounds: :return: """ while True: logger.debug("start playing game") self.play(start_player_index, whole_rounds) logger.debug("game finished") try: self.endless_play_control.acquire() # timeout in case something goes wrong in the reset, or reset is not called for any reason. # In the normal case we just want to continue playing received = self.endless_play_control.wait(0.01) if received: logger.debug("endless play received control message") else: logger.debug( "endless play did not receive control message! Timeout occurred. Endless play resuming." ) if self.stop_playing: logger.debug("stopping endless play") break finally: self.endless_play_control.release() logger.debug("reset game") self.reset() def reset(self): """ Resets the game so that a new game can be started. Used in the endless mode :return: """ self.reset_points() self.stiche = [] def play(self, start_player_index=0, whole_rounds=False): """ Plays a game from the start to the end in the following manner: 1. The dealer shuffles the cards 2. The dealer deals 9 cards to each player 3. The player on the right side of the dealer chooses the trumpf. If he/she chooses 'geschoben' his/her partner can choose the trumpf. 4. For 9 rounds/stichs let the players play their cards. 5. After each stich count the points, update the starting player based on who won the stich and add the cards played in the stich to the already played stichs. 6. Check if a team has reached the point limit :param start_player_index: :param whole_rounds: :return: """ if self.seed is not None: # Increment seed by one so that each game is different. # But still the sequence of games is the same each time self.seed += 1 self.dealer.shuffle_cards(self.seed) self.dealer.deal_cards() self.define_trumpf(start_player_index=start_player_index) logger.info('Chosen Trumpf: {0} \n'.format(self.trumpf.name)) for i in range(9): stich = self.play_stich(start_player_index) self.count_points(stich, last=(i == 8)) logger.info('\nStich: {0} \n'.format(stich.player)) logger.info('{}{}\n'.format('-' * 180, self.trumpf)) start_player_index = self.players.index(stich.player) self.stiche.append(stich) self.stich_over_information() if (self.teams[0].won(self.point_limit) or self.teams[1].won( self.point_limit)) and not whole_rounds: return True return False def define_trumpf(self, start_player_index): """ Sets the trumpf based on the choice of the player assigned to choose the trumpf :param start_player_index: The player which is on the right side of the dealer :return: """ is_allowed_trumpf = False generator = self.players[start_player_index].choose_trumpf( geschoben=self.geschoben) chosen_trumpf = next(generator) if chosen_trumpf == Trumpf.SCHIEBEN: self.geschoben = True generator = self.players[(start_player_index + 2) % 4].choose_trumpf(geschoben=self.geschoben) chosen_trumpf = next(generator) while not is_allowed_trumpf: is_allowed_trumpf = trumpf_allowed(chosen_trumpf=chosen_trumpf, geschoben=self.geschoben) trumpf = generator.send(is_allowed_trumpf) chosen_trumpf = chosen_trumpf if trumpf is None else trumpf self.trumpf = chosen_trumpf return self.trumpf def play_stich(self, start_player_index): """ Plays one entire stich :param start_player_index: the index of the player who won the last stich or was assigned to choose the trumpf :return: the stich containing the played cards and the winner """ self.cards_on_table = [] first_card = self.play_card(table_cards=self.cards_on_table, player=self.players[start_player_index]) self.move_made(self.players[start_player_index].id, first_card) self.cards_on_table = [ PlayedCard(player=self.players[start_player_index], card=first_card) ] for i in get_player_index(start_index=start_player_index): current_player = self.players[i] card = self.play_card(table_cards=self.cards_on_table, player=current_player) self.move_made(current_player.id, card) self.cards_on_table.append( PlayedCard(player=current_player, card=card)) stich = stich_rules[self.trumpf](played_cards=self.cards_on_table) return stich def play_card(self, table_cards, player): """ Checks if the card played by the player is allowed. If yes removes the card from the players hand. :param table_cards: :param player: :return: the card chosen by the player """ cards = [played_card.card for played_card in table_cards] is_allowed_card = False generator = player.choose_card(state=self.get_status()) chosen_card = next(generator) while not is_allowed_card: is_allowed_card = card_allowed(table_cards=cards, chosen_card=chosen_card, hand_cards=player.cards, trumpf=self.trumpf) card = generator.send(is_allowed_card) chosen_card = chosen_card if card is None else card else: logger.info('Table: {0}:{1}'.format(player, chosen_card)) player.cards.remove(chosen_card) return chosen_card def move_made(self, player_id, card): for player in self.players: player.move_made(player_id, card, self.get_status()) def stich_over_information(self): [player.stich_over(state=self.get_status()) for player in self.players] def count_points(self, stich, last): """ Gets the team of the winner of the stich and counts the points. :param stich: :param last: True if it is the last stich of the Game, False otherwise :return: """ stich_player_index = self.players.index(stich.player) cards = [played_card.card for played_card in stich.played_cards] self.add_points(team_index=(stich_player_index % 2), cards=cards, last=last) def add_points(self, team_index, cards, last): """ Adds the points of the cards to the score of the team who won the stich. :param team_index: :param cards: :param last: :return: """ points = count_stich(cards, self.trumpf, last=last) points = points * counting_factor[ self.trumpf] if self.use_counting_factor else points self.teams[team_index].points += points def get_status(self): """ Returns the status of the game in a dictionary containing - the stiche - the trumpf - if it has been geschoben - the point limit - the cards currently on the table - the teams :return: """ return dict(stiche=[stich_dict(stich) for stich in self.stiche], trumpf=self.trumpf.name, geschoben=self.geschoben, point_limit=self.point_limit, table=[ played_card_dict(played_card) for played_card in self.cards_on_table ], teams=[dict(points=team.points) for team in self.teams]) def reset_points(self): """ Resets the points of the teams to 0. This is used when single games are played. :return: """ [team.reset_points() for team in self.teams]
class DataLoader: """ Class for loading data Attributes: num_processor: an integer indicating the number of processors for loading the data, normally 4 is enough capacity: an integer indicating the capacity of the data load queue, default set to 10 batch_size: an integer indicating the batch size for each extraction from the data load queue phase: an string indicating the phase of the data loading process, can only be 'train' or 'test' """ def __init__(self, num_processor, batch_size, phase, batch_idx_init=0, data_ids_init=train_ids, capacity=10): self.num_processor = num_processor self.batch_size = batch_size self.data_load_capacity = capacity self.manager = Manager() self.batch_lock = Lock() self.mutex = Lock() self.cv_full = Condition(self.mutex) self.cv_empty = Condition(self.mutex) self.data_load_queue = self.manager.list() self.cur_batch = self.manager.list([batch_idx_init]) self.processors = [] if phase == 'train': self.data_ids = self.manager.list(data_ids_init) elif phase == 'test': self.data_ids = self.manager.list(test_ids) else: raise ValueError('Could not set phase to %s' % phase) def __load__(self): while True: image_dicts = [] self.batch_lock.acquire() image_ids = self.data_ids[self.cur_batch[0] * self.batch_size:(self.cur_batch[0] + 1) * self.batch_size] self.cur_batch[0] += 1 if (self.cur_batch[0] + 1) * self.batch_size >= len(self.data_ids): self.cur_batch[0] = 0 random.shuffle(self.data_ids) self.batch_lock.release() self.cv_full.acquire() if len(self.data_load_queue) > self.data_load_capacity: self.cv_full.wait() self.data_load_queue.append(get_data(image_ids)) self.cv_empty.notify() self.cv_full.release() def start(self): for _ in range(self.num_processor): p = Process(target=self.__load__) p.start() self.processors.append(p) def get_batch(self): self.cv_empty.acquire() if len(self.data_load_queue) == 0: self.cv_empty.wait() batch_data = self.data_load_queue.pop() self.cv_full.notify() self.cv_empty.release() return batch_data def get_status(self): self.batch_lock.acquire() current_cur_batch = self.cur_batch[0] current_data_ids = self.data_ids self.batch_lock.release() return { 'batch_idx': int(current_cur_batch), 'data_ids': list(current_data_ids) } def stop(self): for p in self.processors: p.terminate()
class PolomaBuff: def __init__(self, table, workers=cpu_count(), maxconn=cpu_count(), maxbuff=50000, batchsize=5000, *args, **kwargs): self.table = table self.maxbuff = maxbuff self.maxconn = maxconn self.batchsize = batchsize self._args = args self._kwargs = kwargs self._queue = Queue() self._buffer_notifier = Condition() self._conn_notifier = Condition() self._conns = Value('i', 0) self._buffsize = Value('i', 0) self._sent = Value('i', 0) self._workers = 0 self._buffer = [] self._procs = [] self._spawn(workers) self._progress() def _progress(self): print('\tSENT:', self._sent.value, 'BUFFER:', self._buffsize.value, 'CONNS:', self._conns.value, 'WORKERS:', self._workers, 'CPU:', cpu_percent(), 'MEM:', virtual_memory().percent, ' ' * 10, end='\r') def _spawn(self, workers): for _ in range(workers): values = (self._sent, self._buffsize, self._conns, self._queue, self._buffer_notifier, self._conn_notifier) p = Process(target=self._worker, args=values) p.daemon = True self._procs.append(p) p.start() self._workers += 1 def _worker(self, _sent, _buffsize, _conns, _queue, _buffer_notifier, _conn_notifier): def _wait_if_max_conns(): _conn_notifier.acquire() while _conns.value >= self.maxconn: _conn_notifier.wait() _conn_notifier.release() def _send(_conn_notifier, _conns, _sent, _buffer): _conns.value += 1 c = PolomaConn(*self._args, **self._kwargs) is_nested(_buffer) c.insert(self.table, _buffer) c.commit() c.close() _conns.value -= 1 _notify(_conn_notifier) _sent.value += len(_buffer) def _notify(notifier): notifier.acquire() notifier.notify() notifier.release() while True: _buffer = _queue.get() _buffsize.value -= len(_buffer) _notify(_buffer_notifier) if _buffer == 'KILL': break _wait_if_max_conns() Thread(target=_send, args=(_conn_notifier, _conns, _sent, _buffer)).start() def _wait_if_buff_full(self): self._buffer_notifier.acquire() while self._buffsize.value >= self.maxbuff: self._buffer_notifier.wait() self._buffer_notifier.release() def append(self, item, batch=False): if batch: self._buffer += item else: self._buffer.append(item) self._wait_if_buff_full() if len(self._buffer) >= self.batchsize: self._buffsize.value += len(self._buffer) self._queue.put(self._buffer) self._progress() self._buffer = [] def kill(self): for _ in range(self._workers): self._queue.put('KILL') for p in self._procs: p.join() print()
class TProcessPoolServer(TServer): """Server with a fixed size pool of worker subprocesses to service requests Note that if you need shared state between the handlers - it's up to you! Written by Dvir Volk, doat.com """ def __init__(self, *args): TServer.__init__(self, *args) self.numWorkers = 10 self.workers = [] self.isRunning = Value('b', False) self.stopCondition = Condition() self.postForkCallback = None def setPostForkCallback(self, callback): if not callable(callback): raise TypeError("This is not a callback!") self.postForkCallback = callback def setNumWorkers(self, num): """Set the number of worker threads that should be created""" self.numWorkers = num def workerProcess(self): """Loop getting clients from the shared queue and process them""" if self.postForkCallback: self.postForkCallback() while self.isRunning.value: try: client = self.serverTransport.accept() if not client: continue self.serveClient(client) except (KeyboardInterrupt, SystemExit): return 0 except Exception as x: logger.exception(x) def serveClient(self, client): """Process input/output from a client for as long as possible""" itrans = self.inputTransportFactory.getTransport(client) otrans = self.outputTransportFactory.getTransport(client) iprot = self.inputProtocolFactory.getProtocol(itrans) oprot = self.outputProtocolFactory.getProtocol(otrans) try: while True: self.processor.process(iprot, oprot) except TTransportException as tx: pass except Exception as x: logger.exception(x) itrans.close() otrans.close() def serve(self): """Start workers and put into queue""" # this is a shared state that can tell the workers to exit when False self.isRunning.value = True # first bind and listen to the port self.serverTransport.listen() # fork the children for i in range(self.numWorkers): try: w = Process(target=self.workerProcess) w.daemon = True w.start() self.workers.append(w) except Exception as x: logger.exception(x) # wait until the condition is set by stop() while True: self.stopCondition.acquire() try: self.stopCondition.wait() break except (SystemExit, KeyboardInterrupt): break except Exception as x: logger.exception(x) self.isRunning.value = False def stop(self): self.isRunning.value = False self.stopCondition.acquire() self.stopCondition.notify() self.stopCondition.release()
cond = Condition() try: for x in range(0, threads): p = Process(target=worker, args=(x, v, cond)) jobs.append(p) p.start() for i in range(0, 10000): while v.value < (threads): #print "M %d sync %d" % (v.value,threads) time.sleep(0.01) print "M %d sync done %d" % (v.value, threads) cond.acquire() cond.notify_all() cond.release() with v.get_lock(): v.value = 0 for j in jobs: j.join() except Exception as error: sys.exit(1) for j in jobs: j.join() except Exception as error: sys.exit(1)
class TProcessPoolServer(TServer): """ Server with a fixed size pool of worker subprocesses which service requests. Note that if you need shared state between the handlers - it's up to you! Written by Dvir Volk, doat.com """ def __init__(self, * args): TServer.__init__(self, *args) self.numWorkers = 10 self.workers = [] self.isRunning = Value('b', False) self.stopCondition = Condition() self.postForkCallback = None def setPostForkCallback(self, callback): if not callable(callback): raise TypeError("This is not a callback!") self.postForkCallback = callback def setNumWorkers(self, num): """Set the number of worker threads that should be created""" self.numWorkers = num def workerProcess(self): """Loop around getting clients from the shared queue and process them.""" if self.postForkCallback: self.postForkCallback() while self.isRunning.value == True: try: client = self.serverTransport.accept() self.serveClient(client) except (KeyboardInterrupt, SystemExit): return 0 except (Exception) as x: logging.exception(x) def serveClient(self, client): """Process input/output from a client for as long as possible""" itrans = self.inputTransportFactory.getTransport(client) otrans = self.outputTransportFactory.getTransport(client) iprot = self.inputProtocolFactory.getProtocol(itrans) oprot = self.outputProtocolFactory.getProtocol(otrans) try: while True: self.processor.process(iprot, oprot) except (TTransportException) as tx: pass except (Exception) as x: logging.exception(x) itrans.close() otrans.close() def serve(self): """Start a fixed number of worker threads and put client into a queue""" #this is a shared state that can tell the workers to exit when set as false self.isRunning.value = True #first bind and listen to the port self.serverTransport.listen() #fork the children for i in range(self.numWorkers): try: w = Process(target=self.workerProcess) w.daemon = True w.start() self.workers.append(w) except (Exception) as x: logging.exception(x) #wait until the condition is set by stop() while True: self.stopCondition.acquire() try: self.stopCondition.wait() break except (SystemExit, KeyboardInterrupt): break except (Exception) as x: logging.exception(x) self.isRunning.value = False def stop(self): self.isRunning.value = False self.stopCondition.acquire() self.stopCondition.notify() self.stopCondition.release()
class ExternalPlayer(GreedyPlayer): """ The RL player in the gym environment wants to initiate control by invoking the step() function. This step function sends an action, lets the environment simulate and then receives an observation back from the environment. In this schieber environment the control is initiated by the Game and not by the player. This is why we need this architecture with this external player. The external player blocks when its choose_card() method is called and sends the current state received by the Game as an observation to the rl player from gym who connects via a websocket. Then the rl agent selects an action and sends it back to this external player. The external player submits this action as the chosen card to the Game. The Game simulates the game and this process starts over. With the help of this architecture we can use the benefits of the standardized gym environments with many rl methods which are already implemented (openai baselines: https://github.com/openai/baselines). """ def __init__(self, name='unknown', seed=None, trumps='all'): super().__init__(name, seed, trumps) self.action_received = Condition() self.observation_received = Condition() self.action = {} self.observation = {} def choose_card(self, state=None): """ Chooses the card and verifies if the chosen card is allowed to be played in the current game state. :param state: :return: """ # if self.at_last_stich(): # allowed = yield self.cards[0] # else: self.observation_received.acquire() self.observation = self.build_observation(state, self.cards) logger.debug(f"choose_card received observation: {self.observation}") self.observation_received.notify_all() # notify all threads to be sure self.observation_received.release() self.action_received.acquire() received = self.action_received.wait() if not received: logger.debug( "Timeout occurred. action_received condition has not been notified." ) logger.debug(f"choose_card received action: {self.action}") allowed_cards = self.allowed_cards(state=state) chosen_card = allowed_cards[ 0] # set chosen_card to the first allowed card in case anything goes south chosen_card = self.set_chosen_card(allowed_cards, chosen_card) self.action_received.release() allowed = yield chosen_card if allowed: yield None @staticmethod def build_observation(state, cards): observation = state observation["cards"] = cards return observation def set_chosen_card(self, allowed_cards, chosen_card): """ Sets the chosen card based on the action of the RL player. :param allowed_cards: :param chosen_card: :return: """ if self.action is not None: if self.action in allowed_cards: logger.info(f"Successfully chose the card: {self.action}") chosen_card = self.action else: logger.error( f"{self.action} is not a valid card! Choosing the first allowed card now." ) else: logger.debug("chosen card is None") return chosen_card def get_observation(self): """ Gets the observation obtained by the game :return: """ self.observation_received.acquire() received = self.observation_received.wait(0.01) if not received: print( "Timeout occurred. observation_received condition has not been notified." ) observation = self.observation logger.debug(f"get_observation {observation}") self.observation_received.release() return observation def set_action(self, action): """ Sets the action chosen by the RL player :param action: :return: """ if self.hand_empty(): logger.error( "set_action: There are no cards on my hand, so I cannot choose any card!" ) self.action_received.acquire() self.action = action logger.debug(f"set_action: {self.action}") self.action_received.notify_all() # notify all threads to be sure self.action_received.release() def before_first_stich(self): """ Checks if the player has already played any cards in this game :return: """ return len(self.cards) == 9 def at_last_stich(self): """ Checks if the player is at the last stich where there is no choice anymore :return: """ return len(self.cards) == 1 def hand_empty(self): """ Checks if the hand is empty or if there are any cards left. :return: """ return len(self.cards) == 0