2

I'm writing a Linux driver for a USB HID device in Python. The device has two ways it sends data, both of which are needed: feature reports (synchronous) and input reports (asynchronous). Using the hidapi Cython library I have only one instance of the device to work with, but I need to set up a listener for hid.read() that will run constantly AND allow synchronous methods to be called at will by the application to send feature reports.

Currently I have the listener in one thread and the synchronous calls in another. When I run the program, my synchronous calls are not happening, though they work fine if I never start the listener; so it appears the listener thread is taking over.

Below is the troubling piece of code:

app.py

# app.py
import threading
import time
import myhiddriver

# Code that sets mydevice

class Reader:
  def start(self, device):
    self.requests = myhiddriver.DeviceRequest(device)
    # Wait a bit before firing, for testing
    time.sleep(3)
    self.requests.request_swipe_card()

  def start_listener(self, device):
    self.listener = myhiddriver.DeviceListener(device)

reader = Reader()
thread1 = threading.Thread(target=reader.start, args=(mydevice,))
thread1.daemon = True
thread2 = threading.Thread(target=reader.start_listener, args=(mydevice,))
thread2.daemon = True
thread1.start()
thread2.start()

# Keep this puppy running
while True:
  pass

myhiddriver.py

import threading

LOCK = threading.Lock()

class DeviceRequest:
  def __init__(self, device):
    # Lock it up first
    LOCK.acquire()
    self.device = device
    LOCK.release()

  def request_swipe_card(self):
    # Lock this up too
    LOCK.acquire()
    self.device.set_feature_report(insert data here)
    LOCK.release()

class DeviceListener:
  def __init__(self, device):
    # Lock me up
    LOCK.acquire()
    self.device = device
    self.start_listener()
    LOCK.release()

  def start_listener(self):
    while True:
      # Should I be locking this up?
      LOCK.acquire()
      data = self.device.read(255)
      LOCK.release()
      if data:
        print data
      else:
        pass

My Question: Why is my synchronous call (request_swipe_card) not following through? Or, how can I better architect this to have an endless listener AND the ability to make synchronous calls on the same object?

Devin Young
  • 841
  • 7
  • 21

1 Answers1

3

from the looks of the code, it is because you are locking it up

When the state is unlocked, acquire() changes the state to locked and returns immediately. When the state is locked, acquire() blocks until a call to release() in another thread changes it to unlocked

here is the problem:

class DeviceListener:
  def __init__(self, device):
    LOCK.acquire() # first you lock it up
    self.device = device
    self.start_listener()
    LOCK.release()

  def start_listener(self):
    while True: # because of the loop, the lock wouldn't get release even if the LOCK below doesn't exist
      LOCK.acquire() # it end up blocking here and oops, it locked up xD 
      data = self.device.read(255) # so it wouldn't be able to read here
      LOCK.release()
      if data:
        print data
      else:
        pass

and when the request_swipe_card end up being call in the other thread, it end up blocking there too

  def request_swipe_card(self):
    LOCK.acquire() # right here xD
    self.device.set_feature_report(insert data here)
    LOCK.release()
freeforall tousez
  • 836
  • 10
  • 26
  • I wound up going in the direction of multiprocessing since I have the luxury of multiple cores, but this makes sense. Thanks! – Devin Young Nov 08 '14 at 23:44