Simple Application Development Using pThreads

pThread development is the best paridigm for embedded development today.  It has been adopted by every major operating system and embedded operating system vendor.  Millions of developers known and use this approach complete with file and socket I/O.  pThreads are the mainstream.

pThread development is much faster and easier than older proprietary paridigms.  It offers the ability to have easily ported software to any industry standards based environment including: 

  • Windows
  • Linux
  • All mainstream RTOS solutions including VxWorks, Integrity, and QNX
  • Most portable phones and phone operating systems including Android and OSX

All of these operating systems have the same pthread environment.  Most also have processes; however, Unison offers a single process multiple thread implementation which is POSIX class 1 and class 2 compatible.  This eliminates process related calls but does have complete I/O.

Overall, simple application development and maintenance goes hand in hand with pthreads and good software design principles.  These principles include all the steps in the software development life cycle.  They also include all the architectual design rules that lead to flexible and easily maintained software.  These rules coupled with a clear development approach lead directly to great embedded systems.

The best approach to pthread development with file I/O and sockets is to use a design template or design pattern approach.  Although this approach readily maps into UML and Use Cases, this is not a requirement for development in the pthread world.  It does help with describing and implementing larger systems and is a good means of increasing communication within your team.  It gives you the ability to discuss whether the critical rules have been applied. 

The critical rules for architectual design of your embedded software include the following: 

  • A good design is a tree architecture from most abstract to least abstract.
  • Even in C, use object oriented design or abstract data types. 
  • Modules come with both functionality and behavior.
  • Single responsibility for modules (abstract data types).
  • High cohesion within modules (abstract data types).
  • Low coupling between modules (abstract data types).
  • Abstract data types should be open for extension but closed to interface changes.
  • Code to an interface not an implementation (independence).
  • Avoid duplicate code (abstract data types).
  • Delegation allows the use of behaviors without duplication.
  • Composition allows you to choose a behavior from a family of behaviors; generally by using a different implementation of an interface.
  • Composition internalizes the behaviors of the composition so that when the composition goes away, so does the internal behavior.
  • Aggregation of behaviors allows you to use behaviors from another object without destroying the behaviors when the object is terminated.
  • In systems with inheritance, objects should be substitutable for their parents (liskov substitution principle).
  • Actors may be used to characterize system behavior (homomorphic programming).

By applying these rules along with well defined requirements,  a UML diagram, a set of use cases, and a set of design templates, your system design will be clean, decoupled and highly cohesive.  Here are some design patterns you may find useful during this process.

  • Publishers + Subscribers = An Observer Pattern, a 1 to many pattern updating the many when data changes.
  • Decorator Pattern objects dynamically add responsibilities to an object.  Each decorator object HAS-AN object it uses for nested computation. (i.e. stdio fopen)
  • A Monitor Pattern, which comes in various flavors, to serialize access to critical resources.
  • A Server Pattern where a server object waits for multiple requests and may respond in any order.
  • A Transporter Pattern which simply ships data from one object to another cyclically so neither side needs to block.
  • An Aggregator Server Pattern (i.e. iolib open ) where many servers are abstracted into a subsystem and the I/O server is dynamcially selected.
  • A Resource Allocator Pattern (Bank Teller, Ticket Taker) Pattern that allocates resources to a requesting thread.

Using pthreads in your designs along with these simple rules and design patterns leads to high quality maintainable designs.  Life maintaining legacy systems has never been easier.

Contact us for a list of text books and other examples of these patterns.  Simple application development using pthreads is fast and easy.