• No results found

Design and development of a plugin-based architecture on an embedded system

N/A
N/A
Protected

Academic year: 2021

Share "Design and development of a plugin-based architecture on an embedded system"

Copied!
111
0
0

Loading.... (view fulltext now)

Full text

(1)

Institutionen för datavetenskap

Department of Computer and Information Science

Final thesis

Design and development of a plugin-based

architecture on an embedded system

by

Emil Mårtensson

LIU-IDA/LITH-EX-A--13/047--SE

(2)
(3)

Linköping University

Department of Computer and Information Science

Final Thesis

Design and development of a plugin-based

architecture on an embedded system

by

Emil Mårtensson

LIU-IDA/LITH-EX-A--13/047--SE

2013-09-13

Supervisor: Lena Buffoni Examiner: Peter Fritzon

(4)
(5)

På svenska

Detta dokument hålls tillgängligt på Internet – eller dess framtida ersättare –

under en längre tid från publiceringsdatum under förutsättning att inga

extra-ordinära omständigheter uppstår.

Tillgång till dokumentet innebär tillstånd för var och en att läsa, ladda ner,

skriva ut enstaka kopior för enskilt bruk och att använda det oförändrat för

ickekommersiell forskning och för undervisning. Överföring av upphovsrätten

vid en senare tidpunkt kan inte upphäva detta tillstånd. All annan användning av

dokumentet kräver upphovsmannens medgivande. För att garantera äktheten,

säkerheten och tillgängligheten finns det lösningar av teknisk och administrativ

art.

Upphovsmannens ideella rätt innefattar rätt att bli nämnd som upphovsman i

den omfattning som god sed kräver vid användning av dokumentet på ovan

beskrivna sätt samt skydd mot att dokumentet ändras eller presenteras i sådan

form eller i sådant sammanhang som är kränkande för upphovsmannens litterära

eller konstnärliga anseende eller egenart.

För ytterligare information om Linköping University Electronic Press se

förlagets hemsida

http://www.ep.liu.se/

In English

The publishers will keep this document online on the Internet - or its possible

replacement - for a considerable time from the date of publication barring

exceptional circumstances.

The online availability of the document implies a permanent permission for

anyone to read, to download, to print out single copies for your own use and to

use it unchanged for any non-commercial research and educational purpose.

Subsequent transfers of copyright cannot revoke this permission. All other uses

of the document are conditional on the consent of the copyright owner. The

publisher has taken technical and administrative measures to assure authenticity,

security and accessibility.

According to intellectual property law the author has the right to be

mentioned when his/her work is accessed as described above and to be protected

against infringement.

For additional information about the Linköping University Electronic Press

and its procedures for publication and for assurance of document integrity,

please refer to its WWW home page:

http://www.ep.liu.se/

(6)
(7)

Abstract

A startup company is developing a diving computer to help scuba divers to be able to communicate with each other underwater. The company has already developed a custom hardware that can send and receive data to and from each other and needs a software that can handle the transfer and visualize the data. A future revenue stream is to be able to sell “Apps“ to increase the programs feature list and therefor one of the goals is to be able to load a plugin during runtime. There are different ways to implements the plugin system and this thesis focuses on the different designs that exists, their pros and cons and finally show an implementation of a design and why it was chosen. In addition to the plugin system there is also a discussion on the design of the rest of the systems and which workarounds that had to be made due to the restriction of the custom hardware or the plugin design.

(8)

Acknowledgement

I would like to thank Anders Brodin and his company for letting me work on this thesis on his product. He has given me great insight into how to run a startup company and providing me with a different perspective on problems which sometimes help me solve them. I would also like to thank Erik Karlsson and Anders Persson who where my main collages during my thesis work and made the whole experience very enjoyable. Finally I would like to thank Jonas Yngvesson who is an experienced programmer that help me with code reviews.

(9)

Contents

1 Introduction 1

1.1 Background . . . 1

1.2 Limitations . . . 2

1.3 Method and sources . . . 2

1.4 Structure . . . 2

2 All about plugins 3 2.1 What is a plugin . . . 3

2.2 Why should I use plugins? . . . 4

2.3 Plugin Types . . . 5 2.3.1 Stand alone . . . 5 2.3.2 Sandboxed . . . 6 2.3.3 Dynamic library . . . 7 3 C++ 8 3.1 Why c++ . . . 8

3.1.1 The problems with c++ . . . 8

3.1.2 How to make c++ a safer language . . . 10

4 Plugin implementation 13 4.1 The dynamic library approach . . . 13

4.2 Sandboxed . . . 14

4.3 Stand alone . . . 15

4.4 Communication . . . 15

4.4.1 File communication . . . 16

4.4.2 Socket communication . . . 16

4.4.3 Shared memory communication . . . 16

4.4.4 Message Queue communication . . . 17

5 Architecture 18 5.1 My approach . . . 18

5.1.1 Structure . . . 19

5.1.2 Design philosophy . . . 23

(10)

CONTENTS CONTENTS 5.2.1 Bloated interface . . . 24 5.2.2 Loading plugins . . . 25 5.3 Communication . . . 26 5.3.1 IPC communication . . . 27 5.3.2 EventSystem . . . 28 6 Closing 33 6.1 Result . . . 33 6.2 Discussion . . . 34 6.3 Future . . . 34 A Code 38 A.1 MessageQueue . . . 38 A.2 Plugin . . . 45 A.3 PluginManager . . . 50 A.4 EventSystem . . . 57 A.5 USB . . . 76

(11)

List of Figures

2.1 Example of spaghetti code . . . 4

2.2 Example of small classes with specified interfaces. . . 5

2.3 Example of a Stand Alone plugin. . . 5

2.4 Example of a Sandboxed plugins. . . 6

2.5 Speed comparison of the Neighbor-Joining program. [3] . . . 6

2.6 Example of a Dynamic Library plugin. . . 7

3.1 Example of catch everything statement. . . 9

3.2 Heap allocation . . . 9

3.3 Stack allocation . . . 9

3.4 Example for enabling warnings in gcc. . . 10

3.5 Simple factory pattern . . . 10

3.6 Two ways of allocating a large array . . . 11

3.7 Different kinds of pointers. . . 11

3.8 Creating smart pointers. . . 11

3.9 An example of a constrained class. . . 12

4.1 Example of loading a dynamic library. . . 13

4.2 Simple example of using Lua as a sandbox and loading a plugin. 14 4.3 The code I use to fork and execute a plugin. . . 15

4.4 Illustration over file communication. . . 16

4.5 Illustration over shared memory communication. . . 16

4.6 Illustration over shared memory communication. . . 17

4.7 Illustration over message queue communication. . . 17

5.1 Event Signals through the non-plugin part of the architecture. 18 5.2 Overview over the whole system . . . 19

5.3 Network Part . . . 20

5.4 Gui . . . 20

5.5 Sensor Converter . . . 21

5.6 EventSystem connected to a plugin through the PluginManager 21 5.7 Overview of the USB module . . . 22

(12)

LIST OF FIGURES LIST OF FIGURES

5.10 Template for creating a plugin. . . 24

5.11 File structure . . . 25

5.12 Plugin Overview. . . 25

5.13 Event signaled tunneled over the message queue. . . 26

5.14 Example of the Exec command in CLI format. . . 27

5.15 Public interface of EventSource and EventSink . . . 28

5.16 Without any parameters. . . 28

5.17 With two parameters . . . 29

5.18 EventBridge and MessageQueue classes. . . 29

5.19 How the EventBridge works . . . 30

5.20 EventSource referencing a dead EventSink . . . 31

5.21 Double references . . . 31

5.22 EventManager keeping track . . . 32

(13)

Chapter 1

Introduction

1.1

Background

This master thesis is done at a newly created startup company called Aqwary. Their first and only product that is currently under development is a scuba diving console that collects data about its environment and can communi-cate said data to other consoles.

A future goal for Aqwary is to create a online plugin manager like “App-Store” where you can sell different features but right now the biggest concern is how to easily update existing software. My work was to create a system that could easily be extended and updated without necessarily taking the whole system offline andstill be a secure and reliable system.

The solution i propose for this problem is to create as much code as possible as plugins, “Apps”. The reason for the plugin approach is that they are easy to load and unload which helps the updating process. Its also extendable since you can add new functionality to the system even at runtime. If implemented correctly they can have high system reliability because you can easily shutdown faulty parts of the system. If there is something that is true for all software is that they are riddled with bugs and will crash and if you accept that you program will crash then you can try to handle the crash.

Another problem with a regular “everything in one executable“ solution is updating. To update you will have to shutdown the whole system for updating or do some extremely complicated live update. With a plugin system you will only have to shut down the plugin that will receive the update.

(14)

1.2. LIMITATIONS CHAPTER 1. INTRODUCTION

1.2

Limitations

I will try to adhere as much as I can to the c++11 standard and “modern

c++” paradigms[1]. Unfortunately the compiler for the target platform

doesn’t support the complete standards so there will be some workarounds and usage of either c++98 or c for some system calls.

The created the architecture will be able to load different plugins, com-municate data within the system and between the plugins. It will also have some extra parts for reading and displaying sensor values. The creation of the “AppStore” is something for the future and will not be covered in this report.

Due to time constrains I will primarily focus on making the program Linux dependent as it is the target platform instead of platform dependent. The final product will have to fulfill some requirements before commercial-ization since it is an underwater device that will monitor vital values. These requirements will be ignored since the product I am developing is currently a prototype and it would take an unreasonable amount of time.

1.3

Method and sources

I will start by creating a event system for communicating data and some modules that can read and display sensor values. Then I will create a plugin system can load and unload different plugins. Finally i will integrate the event system so that the plugins can send data back and forth between them self and the main application. To create the architecture I will mainly use the knowledge I gathered over time but I will also research specific design choices.

1.4

Structure

This report will start with defining what a plugin is and then list the prob-lems that exist with creating plugins for c++ and different solutions to these problems. Due to time constraints I will not be able to list every problem and solution so I will try to focus on the common ones. After I will describe the architecture I built and which design choices I made and why. Finally the report will end with a summarization of the result and discussion of what I should have done differently and where to continue.

(15)

Chapter 2

All about plugins

2.1

What is a plugin

Dictionary [13] plug-in 1. (computing)

A computer program module or device that interacts with another to add a specific function, or to support a specific file format or device.

A plugin is an optional piece of code that can be enabled and disabled without having to restart, recompile or in any way change the the rest of the code. You should be able to add, remove and update plugins without changing the behavior of the rest of the application with the exception of the part that is dependent on the specific plugin.

Example: If you have a pdf converter and png converter as a plugin and

you remove one of them then the behavior of the program changes since you can not do one of them anymore but the behavior of the other one remains the same.

(16)

2.2. WHY SHOULD I USE PLUGINS?CHAPTER 2. ALL ABOUT PLUGINS

2.2

Why should I use plugins?

There are a couple of reasons why plugins can improve your program[5]. First of all it modularize your code by splitting the code into multiple parts where each part will also need to have a well defined interface or the code will not be reachable. This will reduce the amount of cross references, also called spaghetti code figure 2.1, between different part of the code since all references has to go through the specified interfaces. It will also be easier to update since you will only have to update the part with the change instead of the whole system. In a non plugin based system you usually creates a patch which would bitwise update the executable while in a plugin base system you can still use the same approach or simple replace the whole plugin since the plugins are often quite small in comparison with the main application.

BigClassA BigClassB

Figure 2.1: Example of spaghetti code

If you make the plugin API public or at least semi public then you can outsource some feature development to other developers without giving them access to the whole code base. This is one way you can offload some of the legal responsibility to the plugin developers but you have to be careful because you can still become responsible. The problem with making API public is that you loose some control over your product and if you are not careful then you can be accused of having a faulty product because some one else plugin is faulty. One solution to this problem is to have some kind of plugin store where you can control the quality of the plugins and only allow trusted developers which you have some kind of contract that explicitly states that they take on any responsibility with their plugin code.

(17)

2.3. PLUGIN TYPES CHAPTER 2. ALL ABOUT PLUGINS

As noted, plugins increases the modularity by splitting the code into different parts, see figure 2.2, which can be quite good in some language, especially c++, that suffers in compilation speed when project get very large. If you split the code into plugins then each part of the code get smaller and you will only need to compile the part you change instead of the whole system which can take hours or sometimes even days in a very large systems.

SmallClassC

SmallClassB SmallClassA

SmallClassD

Figure 2.2: Example of small classes with specified interfaces.

2.3

Plugin Types

After some research I have found three basic ways of implementing plug-ins: stand alone, sandboxed and dynamic library. I will use the following definitions when referencing different kind of plugins.

2.3.1

Stand alone

A stand alone program is compiled like a regular program but instead is started by the main program. In Linux this is usually done by forking the main program and then executing the stand alone program (fork and exec). A problem with a stand alone program is that it does not have a direct link, other than it is a child process, to the main program which results in communication problems. There exist ways to communicate between child and parent processes but they are more complicated and less efficient then regular calls in the same application

(18)

2.3. PLUGIN TYPES CHAPTER 2. ALL ABOUT PLUGINS

2.3.2

Sandboxed

The sandbox plugin is a program that lives in its own virtual world, a sandbox, where it can do what ever it wants to do and it will not affect the rest of the system. The sandbox plugin is typically implemented in a scripting language that is interpreted by the main program. It can also be implemented in the main language but it’s often harder.

Figure 2.4: Example of a Sandboxed plugins.

One of the biggest reason to use the sandbox type is safety since the plugin can only access resources that has been externally specified. Unfor-tunately sandboxed plugins are usually slower than a native implementation and especially if they are implemented as a scripting language, see figure 2.5. If you sandbox a different languages than your main one you will have to maintain multiple code bases in different languages which can be problematic since you might need to have different people for the different languages.

(19)

2.3. PLUGIN TYPES CHAPTER 2. ALL ABOUT PLUGINS

2.3.3

Dynamic library

Dynamic library is a library that can be loaded at run time. The regular way is to have the library to be loaded when the main application starts but it can also be loaded at demand. The great thing about dynamic library is that once they are loaded they are a part of your code, figure 2.6 and everything you can do in the main program you can also do in the dynamic library.

Figure 2.6: Example of a Dynamic Library plugin.

Unfortunately this is also one of the bad parts of the dynamic library approach since it hinders the ability to manage faulty plugins. If a loaded plugin is faulty and crashes it will bring down the whole system since there is no clear distinction between the plugin and the rest of the system. This is a major problem for the intended application due to its reliability requirement. The other two approaches has the ability to restart a plugin if it crashes or disable if the faulty plugin is not vital for the application. In this case if a non vital plugin crashes you will have to restart the whole system which will disrupt the service for a longer time then restarting a single plugin.

(20)

Chapter 3

C++

3.1

Why c++

Because c++ is a fast, common and mature language often used in embedded programming it was a requirement from my employer to use it. c++ is a good language for embedded programming since it can be very low level for hardware access and high level for creating good non-implementation specific design. Unfortunately it also has some warts that can make your development process harder and can easily creates unforeseen bugs due to you accidentally does something stupid, that has no logical meaning, but is accepted by the language.

3.1.1

The problems with c++

Plugin support

It currently does not exist any standard way of loading plugins of any kind in c++[12] but there exist workarounds to archive the same effect. To be able to load plugins you have to use some platform dependent system calls. The problem with system calls are platform dependent so you will have to duplicate the depending code if you want to be able to support multiple platforms.

The currently popular operating systems1 uses a c-api for their system

calls which can be natively used in c++ but has some draw backs since it is a different language. One of the draw backs is that you have to limit the data objects that will pass to language barrier to standard layout[12].

(21)

3.1. WHY C++ CHAPTER 3. C++

Safety

c++ is not a safe language in the sense that there is a lot of gotchas and undefined behavior that compiles but makes no sense like returning a refer-ence to a temporary variable. By doing this you will sooner or later crash the system since the variable will most likely be deleted. Pointers are easy to mess up and can lead to unrecoverable crashes if you are lucky. Due to c++ unspecified specification both of these error may or may not lead to an error. Exception can be thrown from anywhere without your knowledge and if they are thrown from the wrong part it will cause std::terminate to be called and the program will stop executing. In language like Java all throwable object inherits from an Exception class so that you can always catch any exception and get a pointer to the exception object while in c++ you can throw any data. To catch all exceptions in c++ you have to use the ”catch ellipse” clause which catches the exception but you do not get any reference to the thrown object3.1.

try { // C o d e h e r e } c a t c h( . . . ) { // E x c e p t i o n h a n d l i n g }

Figure 3.1: Example of catch everything statement.

There exist two memory pools in c++, heap and stack, and both can fail to allocate memory. If the stack fails to allocate is usually due to some logic error like an infinite recursion, figure 3.3, while the heap can fail for any reason. The problem with allocating on the heap is that it is hard to check exactly what went wrong. If the heap allocation in figure 3.2 fails then it is either the new in new string or the class strings constructor that fails. If exceptions are enabled then a std::bad_alloc will be thrown in both cases which give you the information that something failed but not what.

a u t o n a m e = new std :: s t r i n g {" Bob "};

Figure 3.2: Heap allocation

a u t o n a m e = std :: s t r i n g {" Bob "};

(22)

3.1. WHY C++ CHAPTER 3. C++

3.1.2

How to make c++ a safer language

The first thing to do to make c++ a safer language is to enable all warnings and error messages and change your code so it doesn’t emit any. An example can be seen in figure 3.4. With warnings enabled the compiler will complain about returning references to temporary variables and more.

g++ -Wall -Wextra -pedantic <CFLAGS> <FILES> <LDFLAGS>

Figure 3.4: Example for enabling warnings in gcc.

The next thing to do is to use a static analyzer which is a program that read the code and tries to find faults from a logical standpoint. A static analyser can find a lot of small and hard to find errors like reference a deleted variable which is one of the most common causes of faults in larger systems.[2]

Do not use new

c++ can use new or malloc to allocate data on the heap. Also with this type of allocation you get a pointer that you have to remember to delete respec-tively free() when you are done with it or you will start to leak memory. It might sound easy to remember to remove every thing you allocate but sooner or later you reach a state where you have a pointer whose owner is unknown. A simple example where you transfer the ownership of a pointer is the factory pattern 3.5.

W i d g e t * c r e a t e W i d g e t () {

r e t u r n new W i d g e t {}; }

Figure 3.5: Simple factory pattern

The preferred way of dealing with this problem is to allocate on the stack with the RAII pattern[8]. With the assumption that objects has a non-faulty destructor then if the allocation fails, stack overflow, or an exception is thrown while constructing or operating the the object the object will cleanly remove itself without leaking any memory. Creating non-faulty destructors are very easy, the compiler can generate them, if all data members of the object also follows the RAII pattern. Even if the data member does not follow the RAII pattern it is often, not always, quite easy to create a non-faulty destructor since you know which data members need clean up. When it is hard to do the clean up it is often due to non-owning raw-pointers since you do not always now who is responsible for the clean up.

(23)

3.1. WHY C++ CHAPTER 3. C++

But I need the heap

There are some circumstances where you need to allocate on the heap. An example would be to allocate large amount of data, figure 3.6 , or when creator of an object is not the user of said object, figure 3.5. In both cases an appropriate smart pointer can be used instead of normal raw pointers to improve correctness. Also in the latter example you could return by value in c++11 with move semantics[11].

int a r r a y [ 5 0 0 0 ] ;

int* a r r a y = new int[ 5 0 0 0 ] ;

Figure 3.6: Two ways of allocating a large array

A smart pointer is basically a pointer that cleans up after itself. In c++11 they created standardized versions of Boost libraries smart pointers and the two main ones are shared_ptr and unique_ptr, figure 3.7. The difference are that with unique_ptr there can only exist one copy of the pointer and with shared_ptr it can exist multiple copies of the pointer but the object will only be deleted when the last pointer is destroyed.

int* a r r a y = new int[ 5 0 0 0 ] ; // Raw P o i n t e r a u t o a r r a y = std :: s h a r e d _ p t r (new int[ 5 0 0 0 ] ) ; // S m a r t P o i n t e r a u t o a r r a y = std :: u n i q u e _ p t r (new int[ 5 0 0 0 ] ) ; // S m a r t P o i n t e r

Figure 3.7: Different kinds of pointers.

There also exist a helper method for creating shared_ptr that is called std::make_shared, figure 3.8. This function introduces extra safety against memory leaks due to reducing the amount of explicit new.[10] Due to an oversight there is no make_unique in c++11 but there will be in the upcoming standard. To mitigate this I have taken the proposed implementation and made my own make_unique.

a u t o w i d g e t _ p t r = std :: m a k e _ s h a r e d < Widget >() ; // c + + 1 1 a u t o w i d g e t _ p t r = std :: m a k e _ u n i q u e < Widget >() ; // c + + 1 4

(24)

3.1. WHY C++ CHAPTER 3. C++

Exceptions

Exception are a controversial topic in computer science where some people claim that they increases readability and safety since there are no error codes to check or transfer and others claim the opposite since errors can be thrown without your knowledge. They are often disabled in critical system because they lead to non determinable execution path and has an undefined overhead when thrown.

My opinion on the matter is that exceptions should be used sparsely and they shall be well defined and documented. Also code should preferable catch the exception as close to the cause as possible. An example of excep-tion usage from my code, figure 3.9, is when a QueueName is created by an illegal string. The QueueName class takes a string with specific requirements and if these requirements are not met a specified exception will be thrown. The thrown exception also carries a message of which requirement failed. This way a QueueName object is never created with faulty values and can be used in a safe way. If some one tries to create a QueueName with faulty values the exception will be thrown and can be handled at the same place as its creation and not at its usage.

QueueName

+QueueName(name:String)

+toString():String

-string: String

Figure 3.9: An example of a constrained class.

Conclusion

c++ is unsafe but can be made relative safe if you follow some rules. I will try to reduce the amount of heap allocation and when needed I will use a smart pointer for the extra safety. I will not use any raw pointers with the exceptions of out parameters that is designated to be raw pointer in the code standard and when needed to call library code.

I believe they exceptions can be used in a safe way if you specify where and why they are used. Unfortunately there is not any good way to specifies exception in c++ so I will document if a function throws an exception and why[9]. I would have use the new c++11 keyword noexcept on function that I can prove are not throwing any exception but the compiler version for the target platform do not support it.

(25)

Chapter 4

Plugin implementation

As discussed in the plugin chapter there are three basic types of plugins and each one needs to be implemented differently. This chapter will discuss the pros and cons about each implementation in c++.

4.1

The dynamic library approach

If you go with the dynamic library approach you have to open, symlink and finally close the library. In Linux you can do it with dlopen, dlsym and dlclose, see figure 4.1.

int m a i n () { v o i d* h a n d l e = d l o p e n (" ./ h e l l o . so ", R T L D _ L A Z Y ) ; t y p e d e f v o i d(* f u n c t i o n _ t ) () ; // A f u n c t i o n p o i n t e r . f u n c t i o n _ t fun = ( f u n c t i o n _ t ) d l s y m ( handle , " f u n c t i o n _ n a m e ") ; fun () // C a l l i n g the l o a d e d f u n c t i o n . d l c l o s e ( h a n d l e ) ; // C l o s i n g the l i b r a r y . r e t u r n 0; }

Figure 4.1: Example of loading a dynamic library.

The compiler are quite good at finding ”dead code” and removing it from the final executable but when you are creating a dynamic library that will be loaded by another program all the code look like ”dead code”. This feature of the compiler has to be turned off or you end up with an empty executable. If you want the dynamic library to be able to reference the main application you will have to expose its symbols as well as the library’s.

After the trouble of compiling the plugin and the main application so they are loadable and reachable you can load it with specified system calls.

(26)

4.2. SANDBOXED CHAPTER 4. PLUGIN IMPLEMENTATION

main program can. The problem with this is that if the plugin crashes it also crashes the whole system. An example, if a plugin causes a segmenta-tion fault the operating system will kill the whole program due to from its perspective the plugin is the program. A segmentation fault is an unrecov-erable signal the targeted system (Linux) will signal if the code is trying to access memory it doesn’t have access to. A common case where it occurs is when a program is reading or writing to an object that has been deleted.

4.2

Sandboxed

Sandboxed programs are usually run in an emulator or interpreter which means the loading process is different depending on which you choose to use. Usually it is something simple as calling a function loadMyProgram and it will be loaded. The complexity lies in handling dependencies like which program or library has to be loaded first and giving the program access to some of the system, see figure 4.2.

int m a i n () { // I n i t i a t e the s a n d b o x . l u a _ S t a t e * L = l u a _ o p e n () ; l u a o p e n _ i o ( L ) ; l u a o p e n _ b a s e ( L ) ; l u a o p e n _ t a b l e ( L ) ; l u a o p e n _ s t r i n g ( L ) ; l u a o p e n _ m a t h ( L ) ; l u a o p e n _ l o a d l i b ( L ) ;

// L o a d and run the p l u g i n .

int s = l u a L _ l o a d f i l e ( L , " M y P l u g i n . lua ") ; l u a _ c l o s e ( L ) ;

r e t u r n 0; }

Figure 4.2: Simple example of using Lua as a sandbox and loading a plugin. By sandboxing a program you are limiting the scope of capabilities the program can access. This usually has performance penalties but you gain safety since you dictate which part of the system the program has access to. You usually do not write your own sandbox so you have to integrate a premaid system which can be troublesome especially on a uncommon plat-form. The system should preferable be open source or it might be impossible to actually install it or at least very costly.

(27)

4.3. STAND ALONE CHAPTER 4. PLUGIN IMPLEMENTATION

4.3

Stand alone

There is nothing special that needs to be done when compiling a stand alone program. You can use the same settings as the compilation of the main system. The stand alone program will have its own main function and you can launch it by itself. To actually load it as a plugin you will use a system call from your main program to launch it.

The way to do it in Linux is to fork your process and then exec the stand alone program, see figure 4.3. Now the plugin and main program runs in different processes and to send data between you will have to use some kind of inter process communication method.

if(( m _ p i d = f o r k () ) == 0) // C h i l d

{

// The r e a d c h a n n e l w r i t e c h a n n e l are f l i p p e d so t h a t you r e a d // f r o m the c h a n n e l the o t h e r w r i t e s to . e x e c ( m_name , m_name , std :: s t r i n g (" - p l u g i n ") , std :: s t r i n g (" - m q _ c h a n n e l _ r e a d ") , q u e u e _ w r i t e _ n a m e . g e t S t r i n g () , std :: s t r i n g (" - m q _ c h a n n e l _ w r i t e ") , q u e u e _ r e a d _ n a m e . g e t S t r i n g () ) ; e x i t (0) ; }

Figure 4.3: The code I use to fork and execute a plugin.

Because the plugin is its own executable it does not share any code with the main program which means there might be some code duplication. To mitigate this problem you can move duplicated code to shared libraries that can be loaded by both the main program and the plugin. Linux usually only loads one instance of the shared library that can be accessed by both executables.

When a stand alone program crashes the operating system will clean up the acquired resources and signal the parent program that the child died and the reasons why. Depending on the reason you can try to handle the error or just close the faulty plugin.

4.4

Communication

With the exception of the dynamic library approach you can not communi-cate data between plugins directly in c++ for the same reason that you can not load them. In these cases you will have to use some kind of system calls which often results that you have to use c.

(28)

4.4. COMMUNICATION CHAPTER 4. PLUGIN IMPLEMENTATION

4.4.1

File communication

The simplest system call to understand is that one process writes to a file while the other process reads from the same file, see figure 4.4. There are a few problem with this approach where one of them are that reading and writing to a file is often blocking meaning that you can not write to messages and while writing the second the other process is reading the first.

Figure 4.4: Illustration over file communication.

4.4.2

Socket communication

Another way is to use “local sockets“ which is basically a network socket but the data never leaves the local machine. Problem with sockets is that they are blocking which means that there exist one message at a time and if the sender sends two pieces of data the second piece will have to wait until the first one is read. Another part of this problem is that you have to read every message even if you know that the blocking message is outdated and could be skipped.

Plugin Socket

MainProgram

Figure 4.5: Illustration over shared memory communication.

4.4.3

Shared memory communication

There is also shared memory which creates a shared heap where both pro-cesses can read and write data. Unfortunately the regular Linux implemen-tation is quite complex to use and understand. I did not have time to fully

(29)

4.4. COMMUNICATION CHAPTER 4. PLUGIN IMPLEMENTATION

research how to use the shared memory and what benefits it has over other IPC methods. What I can say is that you get ”another” heap that you have to handle and as I discussed in a previous chapter you should prefer to use the stack over the heap.

MainProgram

SharedMemory

Plugin

Figure 4.6: Illustration over shared memory communication.

4.4.4

Message Queue communication

Finally there are message queues which you can describe as a shared stack, see figure 4.7. You put a message on the queue and another process can read it from the queue. The difference between message queues and sockets are that a process can send multiple message on the queue, assuming you are not exceeding the queues size limit, without the receiver having read any messages. You can also put different priorities on each message so that high priority messages can skip lower priority messages.

(30)

Chapter 5

Architecture

5.1

My approach

Since the application has relative high stability requirements there where only two options available, sandboxed and stand alone. The reason the library approach was removed is that if a plugin developer makes a mistake, that mistake can crash the whole program while if you choose one of the other two they can try to handle the crash. The plugins require hardware access which is clunky for a sandboxed plugin, one of the reasons to sandbox is to remove hardware access. This leaves the stand alone approach.

Hardware Usb TDMA GUI Coding/Decoding Sensor Converter

(31)

5.1. MY APPROACH CHAPTER 5. ARCHITECTURE

5.1.1

Structure

The main applications architecture is split in two parts where each part has a slightly different design philosophy. The network part is based on the layers model where each layer can only talk, pass data, to the layer above and below. The rest of the system has a more relaxed model where you can pass data to multiple direction. The reason for this is so the plugins can hijack the signals, do something with them and send them back to the application. EventSystem MyPlugin Plugin EventSystem Code Network Part TDMA Coding/Decoding EventBridge MessageQueue Gui Usb Hardware PluginManager Sensor Converter

(32)

5.1. MY APPROACH CHAPTER 5. ARCHITECTURE

Network Part

The network part with the TDMA and Coding/Decoding module was created by others and the I only integrated them with the rest of the system. It has a two way communication with the USB that will transfer encoded data to and from the hardware which in turn sends it over the network. There is also an input from the sensor converter which have converted the device own values and will be sent over the network. Finally it has an output to the GUI that will be used to visualize values from the network, see figure 5.3. Sensor Converter GUI USB Network Part TDMA Coding/Decoding USB

Figure 5.3: Network Part

GUI

The graphical user interface, GUI, only responsibility is to visualize the sam-pled data. Most of the graphical design was outsource to an GUI consultant while I integrated it with the system and modified it so the values would update. It takes signal from the SensorConverter which will be represented as your own values and from the Network part that will be represented as your friends values, see figure 5.4.

Sensor Converter Gui Network Part Figure 5.4: Gui

(33)

5.1. MY APPROACH CHAPTER 5. ARCHITECTURE

Sensor converter

The sensor converter is the module that will translate sensor values raw format to a human readable format. The module is bounded two three others modules, input USB, output Network Part, output GUI. The USB input reads values from the device own sensor which will be converted and sent to the different outputs, see figure 5.5.

Network Part GUI

USB Sensor Converter

Figure 5.5: Sensor Converter

EventSystem

The EventSystem is used to send data between different parts of the system and to and from different plugins. All named events can be tunneled over to any loaded plugin and back again by sending them over the EventBridge, see figure 5.6.

EventSystem EventBridge PluginManager MessageQueue MyPlugin

Figure 5.6: EventSystem connected to a plugin through the PluginManager

Plugin Manager

The Plugin Manager is the module that keep tracks of all the loaded plugins and make decisions on what to do if a plugin crashes. It is also used for the actual loading and unloading of specified modules. To send messages to and from the loaded plugins you have to use the EventSystem, see figure 5.6.

(34)

5.1. MY APPROACH CHAPTER 5. ARCHITECTURE

USB

All data that is sent or received over either the network or from the sensors are transferred from the custom hardware to the target unit over an USB interface. The USB module is a c++ encapsulation over libusb, which is a c library. The USB has a two way communication with the hardware where it forwards messages from and to the network part. It also reads sensor values and forwards them to the sensor converter, see figure 5.7

Usb

Hardware

Sensor Converter Network Part

Figure 5.7: Overview of the USB module

Hardware

The hardware is a custom made board with different sensors and equipment for sending and receiving signals underwater and it communicates over a USB interface.

Plugin

The plugin module that all plugins have is an encapsulation over the setup and exit code that is required for the plugin system to work. It is also the module that receives messages from the MessageQueue and forwards them to the plugins EventSystem, see figure 5.8.

MessageQueue PluginManager MyPlugin

Plugin EventSystem

Code

(35)

5.1. MY APPROACH CHAPTER 5. ARCHITECTURE

5.1.2

Design philosophy

The overall design is that each module has no knowledge of any other module and you communicates by sending or reading data through an event system, see figure 5.9. If another module is receiving or sending on the same event signal the data will be passed to that module. Multiple modules and plugins are able to receive or send the same signal.

Module A

Module B

EventSystem

Figure 5.9: Example of two bounded modules.

All hardware access has to go through an USB interface due to the actual sensors are on another card. The USB manager reads the data from the USB and signals it on different events. Sensor values will be signal to the the sensor converter which converts the raw values to human readable form while network values will be sent to the Coding/Decoding module.

An example of how the different modules in the main application com-municates can be seen in figure 5.1. Each arrow represent an event signal and the double arrow represents that data can go both ways. With this de-sign you can easily replace modules or move said module to a plugin without affecting the rest of the system.

(36)

5.2. CREATING A PLUGIN CHAPTER 5. ARCHITECTURE

5.2

Creating a Plugin

To make an executable loadable for the plugin architecture you have to instantiate the EventSystem, MessageQueue and exit with the correct code. Also the MessageQueue need correct channel names or you will not be able to send data to and from the plugin. To mitigate all these complexities I created a Plugin class that instantiates everything you need in the correct order and with the correct parameters. The only thing you as a plugin developer has to do is to call exec() so the plugin starts and exit(int status) when you want to close it.

int m a i n (int argc , c h a r** a r g v ) {

std :: vector < std :: string > a r g s ( argv , a r g v + a r g c ) ;

try { P l u g i n p l u g i n ( a r g s ) ; // P l u g i n c o d e g o e s h e r e r e t u r n p l u g i n . e x e c () ; } c a t c h(c o n s t std :: l o g i c _ e r r o r & e ) { r e t u r n P l u g i n :: F A I L E D _ T O _ S T A R T _ P L U G I N ; } c a t c h( . . . ) { r e t u r n P l u g i n :: U N K N O W N _ E X C E P T I O N ; } }

Figure 5.10: Template for creating a plugin.

5.2.1

Bloated interface

A common problem with encapsulation is that they often becomes very ver-bose and complicated to setup with multiple configurations. With my en-capsulation design I choose to remove all options and leaving the setup code as clean as possible, figure 5.10. Since I choose to enable exception in the design the otherwise clean interface still got a bit bloated with try-catch statements. You could reduce it by just using the catch ellipse or ig-noring them but that would hinder the main application from making good decisions on what to do with the plugin when it terminates.

(37)

5.2. CREATING A PLUGIN CHAPTER 5. ARCHITECTURE

5.2.2

Loading plugins

To be able to load a plugin the compiled plugin binary has to be in a sub directory called “plugin” which, see figure 5.11. The reason for this is so that a future improved PluginManager would have an easy time to track where the plugins are without having to rely on some kind of register. The current PluginManager is only able to load a plugin if you know that it exists. A future improvement to the manager would be the ability to browse the plugin folder and see which plugins are there. More improvements would be versioning, updating and installing new plugins.

root/ // The root folder where the software is installed

root/Main // The main executable

root/plugins/ // All the plugins that can be loaded.

root/lib/ // Libraries that are shared between the

// main executable and the plugins

Figure 5.11: File structure

The process of loading a plugin is to fork the current process and execut-ing the plugin executable with parameters specifyexecut-ing which MessageQueue channels you are going to talk on, figure 5.12. The PluginManager manager is designed so it does all this for you and you. The only thing you have to specify the name of the plugin and the PluginManager will load it as long as the plugin has a non-faulty initialization which it should have if you use the code skeleton in figure 5.10.

Main Application

MessageQueue

PluginManager

...

Abstract Plugin

MessageQueue

Plugin

Code

(38)

5.3. COMMUNICATION CHAPTER 5. ARCHITECTURE

5.3

Communication

For sending data between different parts of the system I developed an event system that can pass data between different parts of the system and to dynamically loaded plugins in a fluid way. The concept of the event system is that you externally bind two parts of the code together and one part can signal the other without having any knowledge about the other part or if it even exist.

The reason I developed my own system over using an existing one was so I could easily send signals within the same program and tunnel them to different plugins with the same system. I looked at some libraries like Boost::interprocess but they where a bit over complicated and was hard to integrate with the system. Because I am developing for custom hardware, libraries like Boost are quite hard to configure. If I was more experienced with library configuration it would probably be easier and it would have been a viable option.

MessageQueue Plugin Main Application Module A Module B

Figure 5.13: Event signaled tunneled over the message queue.

I also tried to use other libraries like Plugg and Pluma which are small open source libraries designed to be user friendly and easy to setup. Un-fortunate there where a few problems with them both. For example, Pluma was using C-macros in multiple places which can cause trouble.

Plugg on the other hand looked like something that would work and my own plugin system is taking inspiration from it but at the time it did not have any Linux support and I did not have the time to port it myself. Also since both of these libraries and all others I checked with the exception of sandboxed type plugins use the dynamic library approach which is not viable for my usage.

(39)

5.3. COMMUNICATION CHAPTER 5. ARCHITECTURE

5.3.1

IPC communication

To communicate between plugins I decided to use message queues since they are a common approach to inter process communication (IPC) and they can be non-blocking unlike some other common choices like Unix Sockets. Another common approach would have been to use SharedMemory[6] and I think it would have been a better choice.

Unfortunately due to time constraints I did not have the time to really study it and the little time I did spend on it I found it to look quite com-plicated. In short to use SharedMemory you allocates data on special part of the memory that both processes can access and since regular allocation on the heap can be tricky I would guess that it is extra tricky when you are sharing an extra heap.

MessageQueue

The MessageQueue works by two or more executable connects to the same name channel. The queue works like a stack where you can push and pop messages from it. As said, more then two executable can read form the same queue and therefore each plugin needs to have a unique queue.

exec Plugin -plugin -mq_channel_read r1 -mq_channel_write w1

Figure 5.14: Example of the Exec command in CLI format.

The PluginManager will create two unique name for each plugin and send those names as parameters when the plugin is first is loaded,see fig-ure 5.14. It also sends a -plugin parameter to indicate that the executable is launched as a plugin and not by itself. This is done to hinder accidental launches of a plugin without a main program.

In addition of sending the names the PluginManager will also bind an EventBridge to the MessageQueue that will tunnel event signals over to the plugin. On the plugin side the Plugin class will do the same so any tunneled signals will be read from the MessageQueue and sent to the EventBridge which in turn will send them to any bounded EventSource.

(40)

5.3. COMMUNICATION CHAPTER 5. ARCHITECTURE

5.3.2

EventSystem

EventSource and EventSink

The EventSink and EventSource are two template classes that can accept a variable amount of parameters, figure 5.15. The parameter types dictate which type of data that can be transferred when a event signal is fired. To be explicit on what data you are actually signaling I would recommend using custom types instead of the common one like int, float, etc. The reason for this recommendation is to reduce accidental mix up where an int might mean day of the weak or day of the year.

Signals Params... EventSource -id: int +signal(param: Params) Params... EventSink -id: int +setReceiver(func: Function)

Figure 5.15: Public interface of EventSource and EventSink

The EventSink can specify a function that will be called with the speci-fied types of parameters when the sink is signaled. This function has to have the same function parameters as the sinks template parameter or it will not compile. This is an overarching design choice where code will fail during compile time instead of run time. It is very easy, especially with template programing, where you have some small faulty piece of code which is almost never called and can go unnoticed until the product ships and a customer finds the bug.

a u t o s o u r c e = E v e n t S o u r c e < >() ; a u t o s i n k = E v e n t S i n k < >() ; s i n k . s e t R e c e i v e r ( [ ] ( ) { std :: c o u t < < " E v e n t R e c e i v e d " < < std :: e n d l ; }) ; E v e n t M a n a g e r :: b i n d ( source , s i n k ) ; s o u r c e . s i g n a l () ; // O u t p u t : E v e n t R e c e i v e d

Figure 5.16: Without any parameters.

When the EventSource signal function is called all sinks that is bounded

to that source are signaled, figure 5.16. A single EventSource can be

(41)

5.3. COMMUNICATION CHAPTER 5. ARCHITECTURE

The only restriction applied when binding a source to a sink is they both need to have the same template parameters, see figure 5.17 where the parameters are int and double.

a u t o s o u r c e = E v e n t S o u r c e <int, double>() ;

a u t o s i n k = E v e n t S i n k <int, double>() ;

s i n k . s e t R e c e i v e r ( [ ] (c o n s t int& i , c o n s t d o u b l e& d ) { std :: c o u t < < " E v e n t : " < < i < < " R e c e i v e d . " < < std :: e n d l ; std :: c o u t < < " V a l u e : " < < d < < std :: e n d l ; }) ; E v e n t M a n a g e r :: b i n d ( source , s i n k ) ; s o u r c e . s i g n a l (1 , 3 . 5 ) ; // O u t p u t : E v e n t : 1 R e c e i v e d . // V a l u e : 3.5

Figure 5.17: With two parameters

Each EventSource stores which sinks it is bounded to which means that there is no centralized bottleneck that can cause performance problems if

there are to many events being fired1. In contrast the EventSink only

stores a function that will be called when signaled and not any bounded EventSources. This design decision was to remove circular dependencies between the source and sink but as will discuss in section 5.3.2 this might have been a mistake.

EventBridge

To make an EventSource accessible to the plugins it has to be bounded to an EventBridge that is connected to the plugins. The EventBridge is a class that is connected to an IPC protocol and in this case a MessageQueue. The way it is designed you could switch IPC protocol by instantiating the EventBridge with another protocol, figure 5.18.

MessageQueue

+write(length: size_t, buffer: char*) +read(length: size_t, buffer: char*) EventBridge

-mq: MessageQueue

Figure 5.18: EventBridge and MessageQueue classes.

(42)

nam-5.3. COMMUNICATION CHAPTER 5. ARCHITECTURE

The way the signal tunneling works is that the EventBridge receives bounded signals and sends them them over the IPC to another EventBridge on the plugin side. If there is a matching EventSource bounded to the other bridge then that EventSource will be signaled. Two EventSource will be matched together if you specify both with the same id, see figure 5.19.

There exist only a few named ids that you can use to specify the EventSource which is a draw back. If you could choose the id freely then you would end up with a problem where different plugins used the same id for different signals. This is something that could be improved in the future but I am not sure how without redesigning the system.

Architecture Plugin IPC «Params...» EventSource -id: int +signal(params: Params...) EventBridge -source: List<EventSource> signal(id: int, params: Params...) «Params...» EventSource -id: int +signal(params: Params...) EventBridge -source: List<EventSource> signal(id: int, params: Params...)

Figure 5.19: How the EventBridge works

The reason why the EventBridge is signaling an EventSource instead of an EventSink is to reduce the need to know if a signal is signaled from the plugin or not. By doing this you can build the plugin as the emitting code lies in the plugin when it in reality comes from somewhere else.

(43)

5.3. COMMUNICATION CHAPTER 5. ARCHITECTURE

The lifetime problem

The lifetime problem is that neither the EventSink nor the EventSource lifetime is directly dependent on each other and one can die while the other one still is alive and holds a reference back, see figure 5.20. If an EventSink dies before an EventSource and the EventSource has not been unbounded from the now dead EventSink then the next time the EventSource is sig-naled dead code will be called and the program will depending on the system probably crash.

EventSource

Dead EventSink

Figure 5.20: EventSource referencing a dead EventSink

There are multiple ways of solving this problem can be solved. One of the simplest in understanding is that you should explicitly unbind the EventSink before it is about to die. The problem with this approach is to know when the sink is about to die and you have to unbind it. This is a problem it shares with manual memory management.

Another way of solving the problem is to let the EventSink have ref-erences to all the bounded EventSources and when it is about to die it will automatically unbind the bounded EventSources, see figure 5.21. The problem with this is approach is that you will have double references and circular dependencies which can cause memory leaks in managed languages and is generally problematic in c++.

EventSink

EventSource

(44)

5.3. COMMUNICATION CHAPTER 5. ARCHITECTURE

You could also use some kind of event manager to store all the EventSources, EventSink and there relations with each other. and then let the EventSource query the manager to find which EventSources are bound to it. If you use this solution then you will have a monolithic objects that all EventSources and EventSinks depend upon and you will have to have a fast data structure for the lookups.

EventSink

EventSource

EventManager

Figure 5.22: EventManager keeping track

In retrospect I should have gone with the first option where the EventSink would unregister itself but I wanted to create a more cleaver solution by using std::shared_ptr and std::weak_ptr which would have solve the reference problem. Unfortunately the solution I created was faulty and there was ex-amples where the lifetime problem would still occur and I could not find a way to solve this in the short time I had left.

The “solution“ I finally choose due to time constraint was that you have to manually unbind the EventSource and EventSink which is I think is a bad solution due to the listed problems.

(45)

Chapter 6

Closing

6.1

Result

The resulting architecture is a system that can dynamically load crash man-ageable code and send data between different parts of the system through a specified interface, figure 6.1.

Main Application

EventManager

PluginManager

GUI

TDMA

Coding/Decoding

USB

Hardware

Abstract Plugin

EventManager

Plugin

Code

Figure 6.1: Overview of the system1.

(46)

6.2. DISCUSSION CHAPTER 6. CLOSING

The different part of the system are instantiated in the separately and then bounded together by the event system. This creates a decoupled system with specified interfaces for sending data. By using the event system you can also send said data to plugins which results that there is almost no distinction between regular application code and plugin code.

6.2

Discussion

The biggest surprise to me was how easy it actually was to implement the loading of plugins and how hard it was to actually send data between. My initial thought was that the complexity between those two was reversed where the loading would have been the hardest part.

There was a lot of problems due to the targeted system uses an old compiler. Unfortunately the compiler version I had for the target platform did not support the whole of c++11. This made my goal of applying as much of the “modern c++“ as possible hard. I also encountered a few compiler bugs where a solution that worked on the host machine crashed during compilation for the A lot of time was also spent trying to configure libraries to the target platform and most of the time I ended up writing the code myself.

I spent a lot of time designing, modifying and redesigning the event sys-tem and it is still the part which I am the least happy with. The event system currently blocks until the source has signal all sinks unless the tar-geted sink resides in another plugin. This could probably change so the source copies the data and when it has time, it signals data. This has some consequences that what happens if you fire twice and the first data has not left the source yet. Then you have to buffer and that might be to much work for a simple event system. You could have a buffer of one and a flag to tell the caller that the buffer is full and the caller can decide if it should block or disregard the data. This would probably be a better way since then the buffering is dependent on the caller.

6.3

Future

As noted in the discussion there was quite a few problems with the compiler so one of the first thing I would do if I would continue would be to upgrade the compiler for the targeted platform. This would remove some of the workarounds that had to be made. There is also a new version of c++ that will probably be released next year (2014) which have some improvements. One of the new feature in c++14 is a standardized file system api which could improve the code for loading the plugins.

Also if the work would continue the product would go out of prototype stage and then the requirements that is needed for the underwater device would have to be fulfilled. I have not checked exactly what these

(47)

require-6.3. FUTURE CHAPTER 6. CLOSING

ments are so I do not know how much of the system has to be changed to be certified or if you just have to run the system in different environments. The plugin system is currently Linux based but it should not be to hard to port it to other operating system like Windows due to the abstracted system calls. The hardest part would probably be to change the communi-cation between the main program and its plugins since I use a Linux based protocol. I am not familiar enough with the Windows api to know how different its IPC protocol is. Porting the system to Mac is almost complete since both Mac and Linux are Unix based operating system.

One thing that is lacking from the current plugin system is a versioning number or some king of check code to see if the executable is supported. This is something that has to be implemented when the plugin manager is created or you would not be able to upgrade the software due to incompatibilities.

(48)

Bibliography

[1] Alexandrescu, Andrei (2001). Modern C++ Design: Generic Program-ming and Design Patterns Applied

Addison-Wesley Professional, ISBN-10: 0201704315 [2] Carmack, John. Static Code Analysis

<http://www.altdevblogaday.com/2011/12/24/static-code-analysis/> (Fetched 13-08-28)

[3] Fourment, Mathieu & Gillings, Michael R (2008). A comparison of com-mon programming languages used in bioinformatics.

BMC Bioinformatics, ISSN: 1471-2105

<http://www.biomedcentral.com/content/pdf/1471-2105-9-82.pdf> (Fetched 13-08-28)

[4] Kerrisk Michael (2010). The linux programing interface No Starch Press, ISBN 978-1-59327-220-3

<http://man7.org/tlpi/download/TLPI-52-POSIX Message Queues.pdf>

(Fetched 13-08-28)

[5] McCluskey, Alan (2000). Modularity: upgrading to the next generation design architecture.

Cibbected, ISSN: 1664-834X

<http://www.connected.org/media/modular.html> (Fetched 13-08-28)

[6] Robbins, Kay A. & Robbins, Steven (2003) Unix Systems Programming: Communication, Concurrency and Threads

Prentice Hall Professional Technical Reference, ISBN-10: 0130424110 (Fetched 13-08-28)

(49)

BIBLIOGRAPHY BIBLIOGRAPHY

[7] Sayfan Gigi (2007). Building Your Own Plugin Framework

<http://www.drdobbs.com/cpp/building-your-own-plugin-framework-part/204202899>[Part 1] <http://www.drdobbs.com/cpp/building-your-own-plugin-framework-part/204702751>[Part 2] <http://www.drdobbs.com/cpp/building-your-own-plugin-framework-part/205203129>[Part 3] <http://www.drdobbs.com/cpp/building-your-own-plugin-framework-part/205604762>[Part 4] <http://www.drdobbs.com/cpp/building-your-own-plugin-framework-part/206503957>[Part 5] Dr Dobb’s Journal (Fetched 13-08-28)

[8] Stroustrup, Bjarne. Exception Safety: Concepts and Techniques Springer Berlin Heidelberg, ISSN: 0302-9743

<http://www.stroustrup.com/except.pdf> (Fetched 13-08-28)

[9] Sutter, Herb (2002) A Pragmatic Look at Exception Specifications C/C++ Users Journal, 20(7), July 2002.

<http://www.gotw.ca/publications/mill22.htm> (Fetched 13-08-28)

[10] Sutter Herb (2013). GotW #89 Solution: Smart Pointers <http://herbsutter.com/2013/05/29/gotw-89-solution-smart-pointers/>

(Fetched 13-08-28)

[11] Sutter, Herb (2013). GotW #90 Solution: Factories

<http://herbsutter.com/2013/05/30/gotw-90-solution-factories/> (Fetched 13-08-28)

[12] ISO International Standard ISO/IEC 14882:2011(E) - Programming Language C++.

[13] <https://en.wiktionary.org/wiki/plug-in>

(Fetched 13-07-22, Last modified on 13-06-18 at 01:12) [14] noexcept operator

<http://en.cppreference.com/w/cpp/language/noexcept> (Fetched 13-08-09)

(50)

Appendix A

Code

A.1

MessageQueue

# i f n d e f M E S S A G E _ Q U E U E _ Q U E U E _ N A M E _ H P P # d e f i n e M E S S A G E _ Q U E U E _ Q U E U E _ N A M E _ H P P # i n c l u d e < string > n a m e s p a c e d i d a m u s { // A s i m p l e w r a p p e r a r o u n d std :: s t r i n g t h a t w i l l o n l y a c c e p t // s t r i n g s t h a t can be u s e d as a n a m e in m q _ o p e n . c l a s s Q u e u e N a m e { p u b l i c: // C o n s t r u c t s a Q u e u e N a m e f r o m a std :: s t r i n g if the // std :: s t r i n g has the f o l l o w i n g a t t r i b u t e s . // * The f i r s t c h a r a c t e r is a / ( s l a s h ) .

// * The r e s t of the c h a r a c t e r s do not c o n t a i n a / ( s l a s h ) // * The s t r i n g is n u l l t e r m i n a t e d .

// * The l e n g t h is no m o r e t h e n 255 , not i n c l u d i n g n u l l t e r m i n a t i o n .

// * T h e r e has to be a t l e a s t one c h a r a c t e r a f t e r the i n i t i a l s l a s h . // // p a r a m n a m e : The n a m e t h a t w i l l be t e s t e d . // t h r o w std :: i n v a l i d _ a r g u m e n t : If any of t e s t f a i l s . e x p l i c i t Q u e u e N a m e (c o n s t std :: s t r i n g & n a m e ) ; // Get the i n t e r n a l r e p r e s e n t a t i o n . // r e t u r n : A s t r i n g . std :: s t r i n g c o n s t& g e t S t r i n g () c o n s t; p r i v a t e: // C o n s t r u c t s a new Q u e u e N a m e . // p a r a m n a m e : The n a m e to c o n s t r u c t f r o m . std :: s t r i n g m _ n a m e ; }; } // n a m e s p a c e d i d a m u s # e n d i f

(51)

A.1. MESSAGEQUEUE APPENDIX A. CODE // M o d u l e h e a d e r # i n c l u d e " m e s s a g e _ q u e u e / q u e u e _ n a m e . hpp " // S y s t e m h e a d e r s # i n c l u d e < s t d e x c e p t > n a m e s p a c e d i d a m u s { Q u e u e N a m e :: Q u e u e N a m e (c o n s t std :: s t r i n g & n a m e ) : m _ n a m e ( n a m e ) { a u t o n r _ o f _ c h a r = m _ n a m e . s i z e () ; if ( n r _ o f _ c h a r > 2 5 5 ) t h r o w std :: i n v a l i d _ a r g u m e n t (" Q u e u e N a m e is to l o n g . Max s i z e 2 5 5 . ") ; if ( n r _ o f _ c h a r < 2) t h r o w std :: i n v a l i d _ a r g u m e n t (" Q u e u e N a m e is to s h o r t . Min s i z e 2. ") ; if ( m _ n a m e [0] != ’ / ’) t h r o w std :: i n v a l i d _ a r g u m e n t (" Q u e u e N a m e is m i s s i n g i n i t i a l s l a s h ") ; // I g n o r e the f i r s t c h a r a c t e r s i n c e it is a s l a s h . if ( m _ n a m e . f i n d (’ / ’, 1) != std :: s t r i n g :: n p o s ) t h r o w std :: i n v a l i d _ a r g u m e n t (" Q u e u e N a m e has a d d i t i o n a l s l a s h e s . ") ; } std :: s t r i n g c o n s t& Q u e u e N a m e :: g e t S t r i n g () c o n s t { r e t u r n m _ n a m e ; } } // n a m e s p a c e d i d a m u s

(52)

A.1. MESSAGEQUEUE APPENDIX A. CODE # i f n d e f M E S S A G E _ Q U E U E _ M E S S A G E _ Q U E U E _ H P P # d e f i n e M E S S A G E _ Q U E U E _ M E S S A G E _ Q U E U E _ H P P # i n c l u d e < memory > # i n c l u d e < string > n a m e s p a c e d i d a m u s { c l a s s Q u e u e N a m e ; // M e s s a g e Q u e u e is an a b s t r a c t i o n of U n i x m e s s a g e q u e u e s . c l a s s M e s s a g e Q u e u e { p u b l i c: // C o n s t r u c t s a M e s s a g e Q u e u e w i t h a n a m e . // S y s t e m e r r o r s t h a t can o c c u r are : // p e r m i s s i o n _ d e n i e d :

// The q u e u e exist , but you don ’ t h a v e p e r m i s s i o n to o p e n . // t o o _ m a n y _ f i l e s _ o p e n : // The m a x i m u m n u m b e r of q u e u e s are a l r e a d y o p e n . // t o o _ m a n y _ f i l e s _ o p e n _ i n _ s y s t e m : // The m a x i m u m n u m b e r of q u e u e s are a l r e a d y o p e n . // n o t _ e n o u g h _ m e m o r y : // Out of m e m o r y . // n o _ s p a c e _ o n _ d e v i c e :

// Out of m e m o r y ; see L i n u x MAN m q _ o v e r v i e w (7) // // p a r a m w r i t e _ n a m e : The n a m e the m e s s a g e q u e u e it w i l l w r i t e to . // p a r a m r e a d _ n a m e : The n a m e the m e s s a g e q u e u e it w i l l r e a d f r o m . // t h r o w s std :: s y s t e m _ e r r o r : If an s y s t e m e r r o r o c c u r s . M e s s a g e Q u e u e (c o n s t Q u e u e N a m e & w r i t e _ n a m e , c o n s t Q u e u e N a m e & r e a d _ n a m e ) ; // D e s t r u c t o r ~ M e s s a g e Q u e u e () ; // D e l e t e d C o p y C o n s t r u c t o r . M e s s a g e Q u e u e (c o n s t M e s s a g e Q u e u e &) = d e l e t e; // D e l e t e d C o p y O p e r a t o r . M e s s a g e Q u e u e & o p e r a t o r=(c o n s t M e s s a g e Q u e u e &) = d e l e t e; // M o v e C o n s t r u c t o r . M e s s a g e Q u e u e ( M e s s a g e Q u e u e &&) ; // M o v e o p e r a t o r . M e s s a g e Q u e u e & o p e r a t o r=( M e s s a g e Q u e u e &&) ; p u b l i c: // W r i t e a c h u n k of d a t a to the q u e u e . // S y s t e m e r r o r s t h a t can o c c u r are : // i n t e r r u p t e d : // The c a l l was i n t e r r u p t e d by a s i g n a l h a n d l e r . // p a r a m l e n g t h : The l e n g t h of the d a t a . // p a r a m d a t a : The d a t a to be w r i t t e n . // t h r o w s std :: s y s t e m _ e r r o r : If an e r r o r o c c u r . v o i d w r i t e ( s i z e _ t length , c o n s t c h a r* d a t a ) c o n s t;

(53)

A.1. MESSAGEQUEUE APPENDIX A. CODE

// R e a d s a c h u n k of d a t a f r o m the q u e u e .

// If the s i z e of the d a t a r e a d is not e q u a l to the l e n g t h t h e n // std :: l o g i c _ e r r o r is t h r o w n to i n d i c a t e t h a t the s e n d e r and r e c e i v e r is // out of s y n c . // S y s t e m e r r o r s t h a t can o c c u r are : // i n t e r r u p t e d : // The c a l l was i n t e r r u p t e d by a s i g n a l h a n d l e r . // p a r a m l e n g t h : The l e n g t h of the d a t a . // p a r a m d a t a : The d a t a t h a t was r e a d . // t h r o w s std :: l o g i c _ e r r o r : If a l o g i c e r r o r o c c u r . // t h r o w s std :: s y s t e m _ e r r o r : If an e r r o r o c c u r . v o i d r e a d ( s i z e _ t length , c h a r* d a t a ) c o n s t; // R e a d s a c h u n k of d a t a f r o m the q u e u e .

// If the s i z e of the d a t a r e a d is not e q u a l to the s i z e of t y p e T t h e n a // std :: l o g i c _ e r r o r is t h r o w n to i n d i c a t e t h a t the s e n d e r and r e c e i v e r is // out of s y n c . If an e x c e p t i o n is t h r o w n the d a t a is u n d e f i n e d . // S y s t e m e r r o r s t h a t can o c c u r are : // i n t e r r u p t e d : // The c a l l was i n t e r r u p t e d by a s i g n a l h a n d l e r . // p a r a m d a t a : D a t a t h a t w i l l be r e a d . // t h r o w s std :: l o g i c _ e r r o r : If a l o g i c e r r o r o c c u r . // t h r o w s std :: s y s t e m _ e r r o r : If a s y s t e m e r r o r o c c u r . t e m p l a t e<t y p e n a m e T > v o i d r e a d ( T * d a t a ) c o n s t { a u t o ptr = r e i n t e r p r e t _ c a s t<c h a r* >( d a t a ) ; r e a d (s i z e o f( T ) , ptr ) ; } // W r i t e s a c h u n k of d a t a f r o m the q u e u e . // S y s t e m e r r o r s t h a t can o c c u r are : // i n t e r r u p t e d : // The c a l l was i n t e r r u p t e d by a s i g n a l h a n d l e r . // p a r a m d a t a : D a t a t h a t w i l l be w r i t t e n . t e m p l a t e<t y p e n a m e T > v o i d w r i t e (c o n s t T & d a t a ) c o n s t { a u t o ptr = r e i n t e r p r e t _ c a s t<c o n s t c h a r* >(& d a t a ) ; w r i t e (s i z e o f( T ) , ptr ) ; }

// The max s i z e is the m a x i m u m n u m b e r of b y t e s t h a t can be s e n t o v e r the // the m e s s a g e q u e u e in one p a c k e t . s i z e _ t g e t M a x S i z e () c o n s t; p r i v a t e: std :: s t r i n g m _ r e a d _ n a m e ; std :: s t r i n g m _ w r i t e _ n a m e ;

References

Related documents

Of course our model is a very coarse approximation since the path loss exponent often is larger than two, which favour the multihop architecture On the other hand the energy

När det kommer till WTP för hotellsängen så svarade 45 % av respondenterna att de var villiga att betala 0-20 kronor för att sängen skulle motsvara deras ideal.. Majoriteten (55 %)

The basic time-geographic assumptions concern ideas about the indivisible individual as a study object, and how to handle time in time-space analyses (Hägerstrand 1970a, 1974a,

KMC results demonstrate that, irrespective of the relative occurrence (n–m)/n of unsuccessful simulations (censored data), equilibrium rates extrapolated by both exponential and

När Swenska sprätthöken uppfördes, vilket troligen skedde första gången 1737, kunde publiken beskåda en svensk sprätthök, som klädde sig efter det franska modet och uttryckte

The significant relative intrinsic energy dependence implies that the generic beam quality correction factors calculated using MC are not enough for use in low energy photon

Embedded Systems Linköping Studies in Science and Technology. 1964, 2018 Department of Computer and

Thus, an IPv6-based network layer has been developed on top of the Wavenis API provided by Coronis, using an adaptation layer, 6LoW- PAN, in order to adapt the IPv6 standard to