110 likes | 117 Views
Explore the importance of concurrency and patterns in designing and implementing concurrent and networked systems. Learn about different paradigms and patterns used to manage complexity and improve performance.
E N D
E81 CSE 532S: Advanced Multi-Paradigm Software Development Introduction to Concurrent and Networked Objects Chris Gill Department of Computer Science Washington University, St. Louis cdgill@cs.wustl.edu
Why Study Concurrency and Patterns? • Concurrent/networked systems increasingly important • Significant focus on (multicore) hardware, location independence, scalability, performance, timing etc. • Concurrent/networked Systems are highly challenging to design and implement well • Accidental complexities must be constrained • Limitations with tools, techniques and processes • Reinvention and rediscovery (NIH, “C++ considered harmful”) • Inherent complexities must be accommodated • Network partitions, partial failures, deadlock, race conditions • Patterns and pattern languages help to codify what works • Generative techniques adaptable to new contexts
Categories of Patterns • Service Access and Configuration • Appropriate programming interfaces/abstractions • Concurrency • Exploiting physical and logical parallelism • Synchronization • Managing safety and liveness in concurrent systems • Event Handling • Inescapable in networked systems
Aggregating Paradigms threads, queues, timers OS services OS OS • Single Paradigms • Procedural • Object-Oriented • Generics • Aspect-Oriented • Idioms, Design and Strategic Patterns • A “Multi-Paradigm” Paradigm • i.e., a Multi-Paradigm • i.e., Generative Programming static portability scheduling strategies composition end-to-end aspects OS OS
Procedural Systems Programming ACE_INLINE ACE_thread_t ACE_OS::thr_self (void) { // ACE_OS_TRACE ("ACE_OS::thr_self"); #if defined (ACE_HAS_THREADS) # if defined (ACE_HAS_PTHREADS) // Note, don't use "::" here since the // following call is often a macro. ACE_OSCALL_RETURN (pthread_self (), int, -1); # elif defined (ACE_HAS_STHREADS) ACE_OSCALL_RETURN (::thr_self (), int, -1); # elif defined (ACE_HAS_WTHREADS) return ::GetCurrentThreadId (); # elif defined (ACE_PSOS) // there does not appear to be a way to get // a task's name other than at creation return 0; # elif defined (VXWORKS) return ::taskName (::taskIdSelf ()); # endif /* ACE_HAS_STHREADS */ #else return 1; // Might as well make it the first thread ;-) #endif /* ACE_HAS_THREADS */ } • Common-off-the-shelf (COTS) Operating Systems • E.g., Linux, Windows, VxWorks, LynxOS, … • Standardized APIs • Threads, sockets, … • POSIX, etc. • Portability layer is useful • In C/C++, pre-compilers can do a lot for you From http://www.cs.wustl.edu/~schmidt/ACE_wrappers
Procedural Programming, Continued size_t i; for (i = 0; i < n; i++) { ACE_thread_t t_id; ACE_hthread_t t_handle; int result = ACE_OS:: thr_create (func, arg, flags, &t_id, &t_handle, priority, stack == 0 ? 0 : stack[i], stack_size == 0 ? 0 : stack_size[i], thread_adapter); if (result == 0) { if (thread_ids != 0) thread_ids[i] = t_id; if (thread_handles != 0) thread_handles[i] = t_handle; } else // Bail out if error occurs. break; } return i; • Inherent complexity • Part of the problem itself • Won’t go away • E.g., network latency • Accidental Complexity • Artifact of the solution • Can reduce with better tools and techniques • How many times would you want to (re-)write this code? See also http://www.cs.wustl.edu/~schmidt/PDF/IPC_SAP-92.pdf
Object-Oriented Programming ACE_IPC_SAP • Reduction in Accidental Complexity • Less tedious and error-prone to develop, port, maintain, extend • Inheritance Polymorphism • Allows substitution • Encapsulation • Hides details • Controlled violations may help • E.g., socket handle ACE_SOCK ACE_SPIPE ACE_SOCK_Stream ACE_SOCK_Dgram
Object-Oriented Programming, Continued class Synchronized_Processor { public: int critical_section (); private: Processor p_; Mutex m_; } int Synchronized_Processor:: critical_section () { m_.lock (); int result = p_.do_processing (); m_.unlock (); return result; } • The OO paradigm helps • But, leaves key questions open • When should we create objects? • What are their relationships? • How do they interact? • We still need tactical guidance
Design Patterns Container Iterator * make_iterator()=0; • Each pattern has a name • Part of a higher-level design vocabulary • It applies to a context • It documents a solution • It captures experience • We also need key idioms • E.g., guard, copy-on-write, etc. Array Linked_List Iterator * make_iterator(); Iterator * make_iterator(); template <typename T> class Guard { public: Guard (T & t); ~Guard (); private: T & t_; }; Application of the GoF Factory Method Pattern
Towards Strategic Patterns Sockets? Pipes? Event Multiplexing? Reuse? Portability? • Procedures + OO + Patterns • We’ve made progress on accidental complexity • But still have more to do • And, solutions must respect inherent complexity • Resource access • Communication • Asynchrony • Need strategic guidance to demystify the art
Strategic Patterns Active Objects • Strategic Patterns • Service Access & Configuration • Synchronization • Concurrency • Events • Small pattern-languages • E.g., ACT, Active Object • E.g., Acceptor, Connector, Reactor posts to queues director reactor thread