Concurrent programming is an important part of robotics programming. Multiple inputs, outputs and system resources are often required to be accessed by multiple parts of the code or other parts of the system simultaneously and this can create conflicts. In my current project, TickleMeNAO, poor code design that has not included concurrency has lead to issues with event driven callbacks and resources such as speech recognition. The objective of today is to understand some concepts of conccurent programming.
In [45]:
    
# Imports
import this
from timeit import timeit
    
    
Problem statement:
Coroutines.
Asynchronous Design Techniques - using queues.
Run the code in Choregraph with concurrent tasks each in their own box/timeline.
Thread A sequence of instructions executed within the context of a process.
Process The stuff going on!
Single-threaded Restricting access to a single thread.
Multi-threaded Allowing access to two or more threads.
Concurrency is when two or more tasks can start, run, and complete in overlapping time periods. Could also be two threads that are making progress. An example is multi-tasking (time slicing).
Parallelism A condition that arises when at least two threads are executing simultaneously. An example would be threads on a multicore processor.
From: http://docs.oracle.com/cd/E19455-01/806-5257/6je9h032b/index.html
Concurrency programming as the composition of independently executing processes.
Parallelism programming as the simultaneous execution of, possibly related, computations.
Channels allow communication between processes.
So, concurrency is about dealing with lots of things at once. Parallelism is about doing lots of things at once. Concurrency is a way to structure a program by breaking it into pieces that can be executed independently.
From: http://concur.rspace.googlecode.com/hg/talk/concur.html#slide-4
Thread is used to refer to a seperate path of execution for code.
Process refers to a running executable, can encompass multiple threads.
Task refers to the abstract concept of work that needs to be performed.
Dispatch queue executes tasks, either serially or concurrently, always as a FIFO (first in first out).
Design for concurent programming needs to be done upfront particularly with regard to tasks and data structures. Design steps:
Tips:
Procedural form e.g. a serial state machine:
Threading:
Functional programming e.g. coroutines:
Asynchronous methods e.g. queues:
Greenlets:
Multiprocessing:
In [8]:
    
%%file statemachine.py
""" A simple state machine.
    From: http://www.ibm.com/developerworks/linux/library/l-python-state/index.html
"""
from string import upper
class StateMachine:
    def __init__(self):
        self.handlers = {}
        self.startState = None
        self.endStates = []
    def add_state(self, name, handler, end_state=0):
        name = upper(name)
        self.handlers[name] = handler
        if end_state:
            self.endStates.append(name)
    def set_start(self, name):
        self.startState = upper(name)
    def run(self, cargo):
        try:
            handler = self.handlers[self.startState]
        except:
            raise "InitializationError", "must call .set_start() before .run()"
        if not self.endStates:
            raise "InitializationError", "at least one state must be an end_state"
        while 1:
            (newState, cargo) = handler(cargo)
            if upper(newState) in self.endStates:
                break
            else:
                handler = self.handlers[upper(newState)]
    
    
In [9]:
    
from statemachine import StateMachine
def ones_counter(val):
    print "ONES State:    ",
    while 1:
        if val <= 0 or val >= 30:
           newState = "Out_of_Range"
           break
        elif 20 <= val < 30:
            newState = "TWENTIES"
            break
        elif 10 <= val < 20:
            newState = "TENS"
            break
        else:
            print "  @ %2.1f+" % val,
        val = math_func(val)
    print "  >>"
    return (newState, val)
def tens_counter(val):
    print "TENS State:    ",
    while 1:
        if val <= 0 or val >= 30:
           newState = "Out_of_Range"
           break
        elif 1 <= val < 10:
            newState = "ONES"
            break
        elif 20 <= val < 30:
            newState = "TWENTIES"
            break
        else:
            print "  #%2.1f+" % val,
        val = math_func(val)
    print "  >>"
    return (newState, val)
def twenties_counter(val):
    print "TWENTIES State:",
    while 1:
        if val <= 0 or val >= 30:
           newState = "Out_of_Range"
           break
        elif 1 <= val < 10:
            newState = "ONES"
            break
        elif 10 <= val < 20:
            newState = "TENS"
            break
        else:
            print "  *%2.1f+" % val,
        val = math_func(val)
    print "  >>"
    return (newState, val)
def math_func(n):
    from math import sin
    return abs(sin(n))*31
if __name__== "__main__":
    m = StateMachine()
    m.add_state("ONES", ones_counter)
    m.add_state("TENS", tens_counter)
    m.add_state("TWENTIES", twenties_counter)
    m.add_state("OUT_OF_RANGE", None, end_state=1)
    m.set_start("ONES")
    m.run(1)
    
    
In [281]:
    
%%file threading_example.py
import threading
import time
exitFlag = 0
class myThread (threading.Thread):
    def __init__(self, threadID, name, counter, delay):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.counter = counter
        self.delay = delay
    def run(self):
        print "Starting " + self.name
        print_time(self.name, self.counter, self.delay)
        print "Exiting " + self.name
def print_time(threadName, counter, delay):
    while counter:
        if exitFlag:
            print "%s exited." % threadName
            thread.exit()
        time.sleep(delay)
        print "%s: %s" % (threadName, time.ctime(time.time()))
        counter -= 1
# Create new threads
counter = 10
delay = 0.5
thread1 = myThread(1, "Thread-1", counter, delay)
thread2 = myThread(2, "Thread-2", counter, delay)
# Start new Threads
thread1.start()
thread2.start()
print "Exiting Main Thread"
    
    
In [282]:
    
!python threading_example.py
    
    
In [53]:
    
%%file threading_example_with_lock.py
import threading
import time
class myThread (threading.Thread):
    def __init__(self, threadID, name, counter, delay):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.counter = counter
        self.delay = delay
        
    def run(self):
        print "Starting " + self.name
        # Get lock to synchronize threads
        # nb use of blocking = False arg to allow threads to run in parallel.
        threadLock.acquire(BLOCKING)
        print_time(self.name, self.counter, self.delay, self.threadID)
        # Free lock to release next thread
        try:
            threadLock.release()
        except Exception, e:
            print "Error releasing threadlock: ", e
        
def print_time(threadName, counter, delay, increment):
    global TESTNUM, THREADERROR
    
    while counter:
        time.sleep(delay)
        TESTNUM += increment
        #print "TESTNUM BEFORE: ", TESTNUM
        print "%s: %s" % (threadName, time.ctime(time.time()))
        counter -= 1
        TESTNUM -= increment
        if TESTNUM != 0:
            print "Shared resource was accessed by both threads!"
            THREADERROR = 1
            
def runLockTest(counter, delay):   
    # Create new threads
    thread1 = myThread(1, "Thread-1", counter, delay)
    thread2 = myThread(2, "Thread-2", counter, delay)
    # Start new Threads
    thread1.start()
    thread2.start()
    # Add threads to thread list
    threads.append(thread1)
    threads.append(thread2)
    # Wait for all threads to complete
    for t in threads:
        t.join()
    print "Exiting Main Thread"
    if THREADERROR == 0:
        print "Shared resource properly accessed!"
        
# Test with Lock() blocking enabled.
print "Running with Lock() blocking = True"
threadLock = threading.Lock()
threads = []
BLOCKING = True
TESTNUM = 0
THREADERROR = 0
counter = 10
delay = 0.5
runLockTest(counter, delay)
# Test with Lock() blocking disabled.
print "Running with Lock() blocking = False"
threadLock = threading.Lock()
threads = []
BLOCKING = False
TESTNUM = 0
THREADERROR = 0
counter = 10
delay = 0.5
runLockTest(counter, delay)
    
    
In [54]:
    
!python threading_example_with_lock.py
    
    
Conclusion: If Lock() blocking is True then the threads aren't always well scheduled. If Lock() blocking is false then the shared resource can be accessed both by resourses when it shouldn't be. Let's try a queue.
In [277]:
    
%%file threading_example_with_queue.py
import Queue
import threading
import time
exitFlag = 0
class myThread (threading.Thread):
    def __init__(self, threadID, name, q):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.q = q
    def run(self):
        print "Starting " + self.name
        process_data(self.name, self.q)
        print "Exiting " + self.name
def process_data(threadName, q):
    while not exitFlag:
        queueLock.acquire()
        if not workQueue.empty():
            data = q.get()
            queueLock.release()
            print "%s processing %s" % (threadName, data)
        else:
            queueLock.release()
        time.sleep(1)
threadList = ["Thread-1", "Thread-2", "Thread-3"]
nameList = ["One", "Two", "Three", "Four", "Five", 
            "Six", "Seven", "Eight", "Nine", "Ten"]
queueLock = threading.Lock()
workQueue = Queue.Queue(10)
threads = []
threadID = 1
# Create new threads
for tName in threadList:
    thread = myThread(threadID, tName, workQueue)
    thread.start()
    threads.append(thread)
    threadID += 1
# Fill the queue
queueLock.acquire()
for word in nameList:
    workQueue.put(word)
queueLock.release()
# Wait for queue to empty
while not workQueue.empty():
    pass
# Notify threads it's time to exit
exitFlag = 1
# Wait for all threads to complete
for t in threads:
    t.join()
print "Exiting Main Thread"
    
    
In [278]:
    
!python threading_example_with_queue.py
    
    
Queues provide a way to exchange data between threads.
Conclusion: Threads do not seem a reliable way to handle concurrent tasks as don't run in any particular order. This could be fixed with threading.Condition() perhaps, but conditions also seem problematic.
In [308]:
    
%%file multiprocessing_example.py
from multiprocessing import Process, Lock
import time
    
def print_time(processName, counter, delay, lock, increment):
    global TESTNUM
    
    while counter:
        lock.acquire()
        TESTNUM += increment
        time.sleep(delay)
        print "%s: %s with TESTNUM: %s" % (processName, time.ctime(time.time()), TESTNUM)
        counter -= 1
        TESTNUM -= increment
        if TESTNUM != 0:
            print "Shared resource was accessed by both processes!"
        lock.release()
    
if __name__ == '__main__':
    counter = 10
    delay = 0.5
    TESTNUM = 0
    
    lock = Lock()
    
    # Spawn process objects.
    p1 = Process(target = print_time, args = ('process1', counter, delay, lock, 1))
    p2 = Process(target = print_time, args = ('process2', counter, delay, lock, 2))
    
    # Start processes.
    p1.start()
    p2.start()
    
    # Wait for processes to finish.
    p1.join()
    p2.join()
    
    
In [309]:
    
!python multiprocessing_example.py
    
    
Conclusion: Runs great on the computer. Does not work on the NAO if a proxy is called in the process.
In [3]:
    
# Example 1, using yield.
def countdown(n):
    print "Counting down from", n
    while n > 0:
        yield n
        n -= 1
x = countdown(10)
while 1:
    print x.next()
    
    
    
Example 2, using yield, with data passed back to the generator. It's a couroutine! From: http://antroy.blogspot.co.uk/2007/04/python-coroutines.html
In [36]:
    
# First as a generator only with yield.
def rota(people):
    _people = list(people)
    current = 0
    while len(_people):
        yield _people[current]
        current = (current + 1) % len(_people)
        
people = ["Ant", "Bernard", "Carly", "Deb", "Englebert"]
r = rota (people)
for i in range(10):
    print "It's %s's turn." % r.next()
    
    
In [40]:
    
# Then as a coroutine with send().
def rota(people):
    _people = list(people)
    current = 0
    while len(_people):
        command = yield _people[current]
        current = (current + 1) % len(_people)
        if command:
            comm, name = command
            if comm == "add":
                _people.append(name)
            elif comm == "remove":
                _people.remove(name)
                
def printname(name):
    print "It's %s's turn." % name
    
people = ["Ant", "Bernard", "Carly", "Deb", "Englebert"]
r = rota (people)
print "\nOriginal list\n"
for i in range(6):
    printname(r.next())
    
printname(r.send(("add", "Fred")))
print "\nAdded Fred\n"
for i in range(7):
    printname(r.next())
printname(r.send(("remove", "Deb")))
print "\nRemoved Deb\n"
for i in range(6):
    printname(r.next())
    
    
In [41]:
    
# Example 3, with a generator expression.
line_list = [' line 1\n', 'line 2\n', 'line 3\n']
# Generator expression, returns iterator. Note use of () to define as generator.
stripped_iter = (line.strip() for line in line_list)
# List comprehension, returns a list. Note use of [] to define as list comp.
stripped_list = [line.strip() for line in line_list]
    
In [32]:
    
stripped_iter
    
    Out[32]:
In [33]:
    
stripped_list
    
    Out[33]:
In [34]:
    
while 1:
    print stripped_iter.next()
    
    
    
A few examples from the above concepts, using timeit to time how long the processes take.
In [249]:
    
%%file concurrency_1_general_problem.py
""" Looking at concurrency. Moving two head motors (pitch and yaw),
    and logging data simultaneously.
"""
# MEMORY_VALUE_NAMES is the list of ALMemory values names you want to save.
ALMEMORY_KEY_NAMES = [
"Device/SubDeviceList/HeadYaw/Position/Sensor/Value",
"Device/SubDeviceList/HeadYaw/Position/Actuator/Value",
"Device/SubDeviceList/HeadYaw/ElectricCurrent/Sensor/Value",
"Device/SubDeviceList/HeadYaw/Temperature/Sensor/Value",
"Device/SubDeviceList/HeadYaw/Hardness/Actuator/Value",
"Device/SubDeviceList/HeadYaw/Temperature/Sensor/Status",
"Device/SubDeviceList/HeadPitch/Position/Actuator/Value",
"Device/SubDeviceList/HeadPitch/Position/Sensor/Value",
"Device/SubDeviceList/HeadPitch/ElectricCurrent/Sensor/Value",
"Device/SubDeviceList/HeadPitch/Temperature/Sensor/Value",
"Device/SubDeviceList/HeadPitch/Hardness/Actuator/Value",
"Device/SubDeviceList/HeadPitch/Temperature/Sensor/Status"
]
NAO_IP = "mistcalf.local"
STEPS = 5
from naoqi import ALProxy
def main():
    """ Some simple robot processes.
    """
    
    motion = ALProxy("ALMotion", NAO_IP, 9559)
    posture = ALProxy("ALRobotPosture", NAO_IP, 9559)
    memory = ALProxy("ALMemory", NAO_IP, 9559)
    
    data = list()
    
    # Set stiffness on for Head motors and go to start pose.
    print "Starting move...."
    motion.setStiffnesses("Head", 1.0)
    posture.goToPosture("Crouch", 2.0)
        
    # Core processes. Do some moves and record data.
    for i in range(STEPS):
        positiveAngleStep = 1.0 / STEPS
        negativeAngleStep = -1 * positiveAngleStep
        timeStep = 20 / STEPS
        motion.angleInterpolation(
            ["HeadYaw"],
            [positiveAngleStep],
            [timeStep],
            False
        )
        
        motion.angleInterpolation(
            ["HeadPitch"],
            [negativeAngleStep],
            [timeStep],
            False
        )
        
        line = list()
        for key in ALMEMORY_KEY_NAMES:
            value = memory.getData(key)
            line.append(value)
        data.append(line)
            
    # Gently set stiff off for Head motors and relax.
    print "...Going to stop now!"
    motion.setStiffnesses("Head", 0.0)
    motion.rest()
    print data
    
if __name__ == "__main__":
    main()
    
    
In [10]:
    
!python concurrency_1_general_problem.py
    
    
In [43]:
    
%%file concurrency_1_general_problem_with_timeit.py
""" Looking at concurrency. Moving two head motors (pitch and yaw),
    and logging data simultaneously.
"""
# MEMORY_VALUE_NAMES is the list of ALMemory values names you want to save.
ALMEMORY_KEY_NAMES = [
"Device/SubDeviceList/HeadYaw/Position/Sensor/Value",
"Device/SubDeviceList/HeadYaw/Position/Actuator/Value",
"Device/SubDeviceList/HeadYaw/ElectricCurrent/Sensor/Value",
"Device/SubDeviceList/HeadYaw/Temperature/Sensor/Value",
"Device/SubDeviceList/HeadYaw/Hardness/Actuator/Value",
"Device/SubDeviceList/HeadYaw/Temperature/Sensor/Status",
"Device/SubDeviceList/HeadPitch/Position/Actuator/Value",
"Device/SubDeviceList/HeadPitch/Position/Sensor/Value",
"Device/SubDeviceList/HeadPitch/ElectricCurrent/Sensor/Value",
"Device/SubDeviceList/HeadPitch/Temperature/Sensor/Value",
"Device/SubDeviceList/HeadPitch/Hardness/Actuator/Value",
"Device/SubDeviceList/HeadPitch/Temperature/Sensor/Status"
]
# GLOBALS - as easier than passing arguments to timeit
NAO_IP = "mistcalf.local"
STEPS = 5
POSITIVEANGLE = 1.0
NEGATIVEANGLE = -1.0
TESTREPS = 1
TIME = 20.0
POSITIVEANGLESTEP = POSITIVEANGLE / STEPS
NEGATIVEANGLESTEP = NEGATIVEANGLE / STEPS
TIMESTEP = TIME / STEPS
if TIMESTEP <= 0.05:
    # memory.getData() should not be called more than every 50ms as slow.
    print "Warning, TIMESTEP too fast for memory.getData(), set to 50ms."
    TIMESTEP = 0.05
# GLOBALS - proxies and locks
motion = None
posture = None
memory = None
threadLock = None
processLock = None
import time
import threading
import multiprocessing
from timeit import timeit
from naoqi import ALProxy
##########################################################
#                   START test1Procedural CODE
##########################################################
def test1Procedural():
    """ Do some moves and record data in a procedural ie
        linear, method.
        
    """
    global motion, posture, memory
    data = list()    
    
    for i in range(STEPS):
        motion.angleInterpolation(
            ["HeadYaw"],
            [POSITIVEANGLESTEP],
            [TIMESTEP],
            False
        )
        
        motion.angleInterpolation(
            ["HeadPitch"],
            [NEGATIVEANGLESTEP],
            [TIMESTEP],
            False
        )
        
        line = list()
        for key in ALMEMORY_KEY_NAMES:
            value = memory.getData(key)
            line.append(value)
        data.append(line)
        
    #print data
    
##########################################################
#                   END test1Procedural CODE
##########################################################
    
##########################################################
#                   START test2Post CODE
##########################################################
    
def test2Post():
    """ Do some moves and record data using built in post.
        
    """
    global motion, posture, memory  
    
    id1 = motion.post.angleInterpolation(
                                    ["HeadYaw"],
                                    [POSITIVEANGLE],
                                    [TIME],
                                    False
    )
        
    id2 = motion.post.angleInterpolation(
                                    ["HeadPitch"],
                                    [NEGATIVEANGLE],
                                    [TIME],
                                    False
    )
        
    data = recordData()
    while (motion.isRunning(id1) and motion.isRunning(id2)):
        pass
    
    #print data
    
def recordData():
    """ Record the data from ALMemory.
    Returns a matrix of values
    """
    global memory
    
    print "Recording data ..."
    
    data = list()
    for i in range (STEPS):
        line = list()
        for key in ALMEMORY_KEY_NAMES:
            value = memory.getData(key)
            line.append(value)
        data.append(line)
        time.sleep(TIMESTEP)
        
    print "Done recording data"
    
    return data
    
##########################################################
#                   END test2Post CODE
##########################################################
##########################################################
#                   START test3Threading CODE
##########################################################
class myThread(threading.Thread):
    def __init__(self, threadID):
        threading.Thread.__init__(self)
        self.threadID = threadID  
        
    def run(self):
        if self.threadID == 1:
            headYawMotion()
        elif self.threadID == 2:
            headPitchMotion()
        elif self.threadID == 3:
            data = recordDataForThread()
            # print data
        else:
            print "oops, no thread"
def headYawMotion():
    global motion, posture, memory, threadLock
    
    for i in range(STEPS):
        threadLock.acquire(True)
        motion.angleInterpolation(
            ["HeadYaw"],
            [POSITIVEANGLESTEP],
            [TIMESTEP],
            False
        )
        threadLock.release()
    
def headPitchMotion():
    global motion, posture, memory, threadLock
    
    for i in range(STEPS):
        threadLock.acquire(True)
        motion.angleInterpolation(
                                ["HeadPitch"],
                                [NEGATIVEANGLESTEP],
                                [TIMESTEP],
                                False
        )
        threadLock.release()
    
def recordDataForThread():
    """ Record the data from ALMemory.
    Returns a matrix of values
    """
    global motion, posture, memory, threadLock
    
    print "Recording data ..."
    
    data = list()
    for i in range (STEPS):
        threadLock.acquire(True)
        line = list()
        for key in ALMEMORY_KEY_NAMES:
            value = memory.getData(key)
            line.append(value)
        data.append(line)
        time.sleep(TIMESTEP)
        threadLock.release()
        
    print "Done recording data"
    
    return data
def test3Threading():
    """ Do some moves and record data using the threading module.
        Runs without Lock(), will use Lock() as best practise,
        and needed in current app.
        Lock.Acquire(False) with Blocking = False arg seems to run smoother, but
        should be set to True to ensure synchronous running. Requires checking.        
        Lock can also be acquired and released using 'with lock:'.
        But without blocking the shared resource does not seem safe.
    
    """
    global motion, posture, memory, threadLock
    
    threads = []
    threadLock = threading.Lock()
    
    # Create new threads.
    thread1 = myThread(1)
    thread2 = myThread(2)
    thread3 = myThread(3)
    
    # Start new threads.
    thread1.start()
    thread2.start()
    thread3.start()
    
    # Wait for threads to end.
    # Essential, or thread calls methods in run, then returns.
    threads.append(thread1)
    threads.append(thread2)
    threads.append(thread3)
    
    for t in threads:
        t.join()
    
##########################################################
#                   END test3Threading CODE
##########################################################
##########################################################
#                   START test4Multiprocessing CODE
# Doesn't run, issues with proxies causing error:
# 5891 qimessaging.remoteobject: no promise found for req id:39  obj: 21  func: 126 type: 2
##########################################################
def headYawMotionProcess(processLock):
    global motion, posture, memory
    
    for i in range(STEPS):
        processLock.acquire()
        motion.angleInterpolation(
                ["HeadYaw"],
                [POSITIVEANGLESTEP],
                [TIMESTEP],
                False
        )
        processLock.release()
    
def headPitchMotionProcess(processLock):
    global motion, posture, memory
    
    for i in range(STEPS):
        processLock.acquire()
        motion.angleInterpolation(
                            ["HeadPitch"],
                            [NEGATIVEANGLESTEP],
                            [TIMESTEP],
                            False
        )
        processLock.release()
    
def recordDataProcess(processLock):
    """ Record the data from ALMemory.
    Returns a matrix of values
    """
    global motion, posture, memory
    
    print "Recording data ..."
    
    data = list()
    for i in range (STEPS):
        processLock.acquire()
        line = list()
        for key in ALMEMORY_KEY_NAMES:
            value = memory.getData(key)
            line.append(value)
        data.append(line)
        time.sleep(TIMESTEP)
        processLock.release()
        
    print "Done recording data"
    
    print data
def test4Multiprocessing():
    """ Do some moves and record data using the multiprocessing module.
    
    """
    global motion, posture, memory, processLock
    
    processLock = multiprocessing.Lock()
    
    # Spawn process objects.
    p1 = multiprocessing.Process(target = headYawMotionProcess, args = (processLock,))
    p2 = multiprocessing.Process(target = headPitchMotionProcess, args = (processLock,))
    p3 = multiprocessing.Process(target = recordDataProcess, args = (processLock,))
    
    # Start processes.
    p1.start()
    p2.start()
    p3.start()
    
    # Wait for processes to finish.
    p1.join()
    p2.join()
    p3.join()
##########################################################
#                   END test4Multiprocessing CODE
##########################################################
##########################################################
#                   START test5Coroutine CODE
##########################################################
def headYawMotionCoroutine(yawMotionList):
    global motion, posture, memory
    
    _yawMotionList = list(yawMotionList)
    current = 0
    
    while len(_yawMotionList):
        angle = _yawMotionList[current][0]
        time = _yawMotionList[current][1]
        motion.angleInterpolation(
                    ["HeadYaw"],
                    [angle],
                    [time],
                    False
        )
        yield
        current += 1
    
def headPitchMotionCoroutine(pitchMotionList):
    global motion, posture, memory
    
    _pitchMotionList = list(pitchMotionList)
    current = 0
    
    while len(_pitchMotionList):
        angle = _pitchMotionList[current][0]
        time = _pitchMotionList[current][1]
        motion.angleInterpolation(
                                ["HeadPitch"],
                                [angle],
                                [time],
                                False
        )
        yield
        current += 1
    
def recordDataCoroutine():
    """ Record the data from ALMemory.
    Returns a matrix of values
    """
    global motion, posture, memory
    
    print "Recording data ..."
    
    data = list()
    # Infinite list as is run every time there is a motion move.
    while 1:        
        line = list()
        for key in ALMEMORY_KEY_NAMES:
            value = memory.getData(key)
            line.append(value)
        data.append(line)
        yield data
        
def test5Coroutine():
    """ Do some moves and record data using coroutines.
    
    """
    global motion, posture, memory
    
    # Generate motion lists
    yawMotionList = []
    pitchMotionList = []            
    yawMotionList = [(POSITIVEANGLESTEP, TIMESTEP) for i in range(STEPS)]
    pitchMotionList = [(NEGATIVEANGLESTEP, TIMESTEP) for i in range(STEPS)]
    
    # Ininitiate coroutine.
    p1 = headYawMotionCoroutine(yawMotionList)
    p2 = headPitchMotionCoroutine(pitchMotionList)
    p3 = recordDataCoroutine()
    # Loop through all steps. Could also be infinite loop if this was,
    # a full coroutine where data was being sent to the lists.
    for i in range(STEPS):
        p1.next()
        p2.next()
        data = p3.next()
        
    #print data
    
##########################################################
#                   END test5Coroutine CODE
##########################################################
def main():
    """ Some simple robot processes.
    """
    global motion, posture, memory
    motion = ALProxy("ALMotion", NAO_IP, 9559)
    posture = ALProxy("ALRobotPosture", NAO_IP, 9559)
    memory = ALProxy("ALMemory", NAO_IP, 9559)    
    # Set stiffness on for Head motors and go to start pose.
    print "Starting tests...."
    motion.setStiffnesses("Head", 1.0)
    print "\n---------------------------------------\n"
    # Goto start position, and run test1Procedural
    print "test1Procedural starting ..."
    posture.goToPosture("Crouch", 2.0)
    t1 = (timeit("test1Procedural()", setup = "from __main__ import test1Procedural", number = TESTREPS))
    print "...end test1Procedural, time: ", t1
    print "\n---------------------------------------\n"
    # Goto start position, and run test2Post
    print "test2Post starting ..."
    posture.goToPosture("Crouch", 2.0)
    t2 = (timeit("test2Post()", setup = "from __main__ import test2Post", number = TESTREPS))
    print "...end test2Post, time: ", t2
    print "\n---------------------------------------\n"
    # Goto start position, and run test3Threading
    print "test3Threading starting ..."
    posture.goToPosture("Crouch", 2.0)
    t3 = (timeit("test3Threading()", setup = "from __main__ import test3Threading", number = TESTREPS))
    print "...end test3Threading, time: ", t3
    print "\n---------------------------------------\n"
    # Goto start position, and run test4Multiprocessing - NOT WORKING
    print "test4Multiprocessing - not working"
    #print "test4Multiprocessing starting ..."
    #posture.goToPosture("Crouch", 2.0)
    #test4Multiprocessing()
    #t4 = (timeit("test4Multiprocessing()", setup = "from __main__ import test3Threading", number = TESTREPS))
    #print "...end test4Multiprocessing, time: ", t4
    print "\n---------------------------------------\n"
    # Goto start position, and run test5Coroutine
    print "test5Coroutine starting ..."
    posture.goToPosture("Crouch", 2.0)
    t5 = (timeit("test5Coroutine()", setup = "from __main__ import test5Coroutine", number = TESTREPS))
    print "...end test5Coroutine, time: ", t5
    print "\n---------------------------------------\n"
    # Gently set stiff off for Head motors and relax.
    print "...ending tests!"
    motion.setStiffnesses("Head", 0.0)
    motion.rest()
    
if __name__ == "__main__":
    main()
    
    
In [44]:
    
!python concurrency_1_general_problem_with_timeit.py
    
    
In [ ]: