Date post: | 03-Jan-2016 |
Category: |
Documents |
Upload: | charity-barber |
View: | 215 times |
Download: | 0 times |
Data Driven ProgrammingChange Nets
Dr. Werner Van Bellee-mail: [email protected]: [email protected]: +47 776 29 40411 November 2005
1. Connections
Connections Multiple 'actors' that can be
separated in time
separated in space
Communicate through connections shared data
self contained (messages)
Different implementation flavors processes, threads, thunks, longjumps, callbacks,...
copy-on-write, fetch-on-access, serialization
Connection Based Programming Provides
loose coupling: every actor can connect any two different actors
actors do not need to know to whom they will be connected
dissociation of control flow and code: send and forget
suitable for distributed systems
efficient local execution (10x slower than function calls)
increased flexibility: rerouting of connections, multiple receivers
Program Layout Programming larger applications consists of
combining methods and functions to bring a program from one state to another
to be modular, often a mixture of action calls ('print out this file') state reactions ('queue contains file to print')
Focus here on state 'reactive' programming a natural use of connections
2. Updates
Updating strategy
1.Change the variable or data content
2.Send out a change notification
3.Listeners receive update and 'react' to the new state
Advantage Efficient for both message based and thread based
connection models.
Disadvantage update loops
Scenario Queue
push(string)
pop()
PrintingQueue relies on a 'waiting' queue for data storage
pops, prints, stops, pops, prints, stops, ...
How to functionally connect these two objects ?How to functionally connect these two objects ?
Scenario Connect a Queue state change to the
PrintingQueue void Queue::push(string s) { files.push_back(s); updated(); }
void PrintingQueue::state_changed(){ if (printing) return; string s = waiting->pop(); printing=true;}
Update Loop atwaiting->pop() !
Update Loop atwaiting->pop() !
direct connection (call / callback)
string Queue::pop() { string s = files.pop_front(); updated();
return s; }
Scenario
void Queue::push(string s) { files.push_back(s); updated(); }
void PrintingQueue::state_changed(){ if (printing) return; string s = waiting->pop(); printing=true;}
indirect connection (Threaded Asgeir style)
All interested partners want to be notified at least once !
All interested partners want to be notified at least once !
Changedpush
poppop
Scenario
void Queue::push(string s) { files.push_back(s); updated(); }
void PrintingQueue::state_changed(){ if (printing) return; string s = waiting->pop(); printing=true;}
indirect connection (message-queue / receiver)
Needless changed messages !Needless changed messages !
Changedpushpop
pop
Changed
pushChanged
Scenario
void Queue::push(string s) { files.push_back(s); updated(); }
void PrintingQueue::state_changed(){ if (printing) return; string s = waiting->pop(); printing=true;}
indirect connection (eventObjects)
Queue state will be accessedconcurrently, popping of change shouldlock queue, deadlocks/livelocks in manyscenarios
Queue state will be accessedconcurrently, popping of change shouldlock queue, deadlocks/livelocks in manyscenarios
pushpop
pop
Changed
pushChanged
Changed
poppush
Update Loops Direct loops avoided using distinct names
poppop_without_update
pop_updatepush_update
pop_from_printer_queuepop_from_other_actor
Leads to unclear naming convention & usage dependency
indirect conflicts with extra actors (log connected to queue)
multi hop loops tend to be difficult to avoid
Update Loops Other solutions include
loop breaking using version numbers
loop breaking using locking
loop breaking points using comparison checks
All of these can work but they tend to be scattered throughout the code !
Control flow of the overall program is difficult to understand
3. Formal State Transition
Petri-Nets Tokens: a piece of data, a message
Petri-Nets Places: contain zero, one or more tokens
Petri-Nets Transitions: can move tokens between places
can fire when all inputs provide a suitable token (pre-condition / guarding)
when fired, removes all input tokens and creates new output tokens (post-condition)
fire atomically (no concurrent transitions)
The Printing Queue Petri-Net
Petri-Nets
Petri-Nets
Petri-Nets Advantages
Good program documentation
Possibility to simulate message flow, without working program
Formal proof of boundedness, reachability (homestate), deadlock-freedom
Efficient implementation through locality-principle
Can we use Petri-nets as a change notification mechanism ?Can we use Petri-nets as a change notification mechanism ?
Tracking Change with Petri-Nets
Petri-Nets Disadvantages
Formal guard requirements are too broad to implement efficiently (search for a matching binding problem).
Programming with Petri-nets difficult because tokens are consumed. All state is volatile.
Petri-nets are good for constraint checkingThey fail to provide natural change tracking capabilities
Petri-nets are good for constraint checkingThey fail to provide natural change tracking capabilities
Change Nets Modification to Petri-nets
There is always exactly one token per place.
Transition are extended with 'might-be-enabled' flag.
'might-be-enabled' is updated whenever any of the input places changes state
Once 'might-be-enabled', the constraint associated with the transition can be checked
Then the transition can be fired
1. Change messages are separated in time -> no direct loop updates2. Change messages are only send if change occurs -> no indirect loops3. Multiple changes result in one change message per listener4. Change messages do no contain the data -> lightweight
1. Change messages are separated in time -> no direct loop updates2. Change messages are only send if change occurs -> no indirect loops3. Multiple changes result in one change message per listener4. Change messages do no contain the data -> lightweight
No update Loops
var<int> a(0); var<int> b(0); var<int> c(0);
a.update_to(b); b.update_to(c); c.update_to(a);
a=10;
Content FireableA B C A-B B-C C-A0 0 0 0 0 0
a=10 10 0 0 1 0 0execute b = a 10 10 0 0 1 0execute c = a 10 10 10 0 0 1execute a = c 10 10 10 0 0 0
Queue
class Queue: public Place{ list<string> files; void push(string s) { files.push_back(s); updated(); } string pop() { string s = files.front(); files.pop_front(); updated(); return s; }};
PrintingQueue
class PrintingQueue{ Queue * waiting; var<bool> printing; PrintingQueue(Queue *q); void state_changed();};
PrintingQueue::PrintingQueue(Queue *q) : waiting(q){new MemberTransition<PrintingQueue>(*this, &PrintingQueue::state) ->react_on(waiting); ->react_on(printing);printing = false;}
PrintingQueue
void PrintingQueue::state_changed(){ if (printing) return; string s = waiting->pop(); printing=true;}
Queue * q = new Queue(); PrintingQueue pq1(q); PrintingQueue pq2(q); q->push("File1"); q->push("File2");
Multi-Threaded Change Nets
1.Transition waits for a changed state
2.Clear state
3.Sorted waiting acquisition of input locks & output locks
4.Checks constraints
5.Execute transition
6.Releases locks
Intermediate Conclusion Petri-nets are a natural way to think about state
changes in programs
Change nets well defined based on Petri-nets (can be automatically generated in function of the constraint net)
Change net transitions provide a clear, transparent way to glue programs together
Change nets can be efficiently implemented in process, thread & thunk based execution models.
4. Implementation
Places
class Place{ // all the listeners to state changes on this place vector<Transition*> output_transitions; public: void update_to(Transition*t) {output_transitions.push_back(t);}; // will update all the output transitions 'changed' state void updated();};
void Place::updated(){ for(int i = 0 ; i < output_transitions.size() ; i++) output_transitions[i].input_place_changed=true;}
Transitions
class Transition{ bool input_place_changed; // might_be_enabled // will create a transition and add it to the global net Transition(); // will mark this transition as 'possible' executable void set_input_place_changed() { input_place_changed=true; }; // will inform this transition to react on changes at place void react_on(Place*p) {p->update_to(this);}; // will check the might_be_enabled state and run if necessary void execute(); virtual void run() = 0;};
void Transition::execute(){ if (!input_place_changed) return; input_place_changed = false; run();}
Convenience Classes - Variables
template <class T> class var: public Place{ T value; public: var<T>() : Place() {}; var<T>(T init) : Place(), value(init) {}; void update_to(var<T> & other); operator const T&(void) {return value;}; operator const T&(void) const {return value;}; var<T>& operator= (const var<T>& f); var<T>& operator= (const T& f);};
Convenience Classes - Variables
template <class T> var<T>& var<T>::operator= (const var<T>& f){
if (value==f.value) return *this;value=f;updated();return * this;
}
Convenience Classes – Member Transitions
template <class H> class MemberTransition: public Transition{ public: typedef void (H::*member)(); member F; H& o; MemberTransition(H &ob, member i) : o(ob) {F=i;}; virtual void run() {((o).*(F))();};};
Conclusion Petri-nets are a natural way to think about state
changes in programs
Change nets well defined based on Petri-nets (can be automatically generated in function of the constraint net)
Change net transitions provide a clear, transparent way to glue programs together
Change nets can be efficiently implemented in process, thread & thunk based execution models.
5. Extra: Trolltech Connections
Connections - Trolltech Multi-threaded
Signals – emit function calls
Slots – accepts function calls
On an i586-500, you can emit around 2,000,000 signals per second connected to one receiver, or around 1,200,000 per second connected to two receivers. The simplicity and flexibility of the signals and slots mechanism is well worth the overhead, which your users won't even notice.
Connections - Trolltech
Connections - Trolltech
#include <QObject>
class Counter : public QObject { Q_OBJECT int m_value; public: Counter() { m_value = 0; } int value() const { return m_value; } public slots: void setValue(int value); signals: void valueChanged(int newValue); };
Connections - Trolltech
void Counter::setValue(int value) { if (value != m_value) { m_value = value; emit valueChanged(value); } }
Connections - Trolltech
class LcdNumber : public QFrame{ Q_OBJECT public: LcdNumber(QWidget *parent = 0); public slots: void display(int num); void display(double num); void display(const QString &str); void setHexMode(); void setDecMode(); void setOctMode(); void setBinMode(); void setSmallDecimalPoint(bool point);};
Connections - Trolltech
Counter * a = new Counter();LcdNumber * l = new LcdNumber();
connect(a,SIGNAL(valueChanged(int)), l,SLOT(display(int)))