Revisión | b9f60973c68c46d2c08fbfc14b2219ee6374aebc (tree) |
---|---|
Tiempo | 2023-04-02 00:31:07 |
Autor | Albert Mietus < albert AT mietus DOT nl > |
Commiter | Albert Mietus < albert AT mietus DOT nl > |
asis fixed syntax and wording
@@ -14,17 +14,16 @@ | ||
14 | 14 | an action on an incoming message (see: :ref:`CCC-Actors`). Depending on ‘:ref:`TheMachinery`’, those events can be |
15 | 15 | queued and this combination *can result* in a **beautiful Heisenbug**. |
16 | 16 | |
17 | - First, let’s explain the Heisenbug, before we give a example. Then we analyse it, show how it can be solved and | |
18 | - finally formulate a *requirement* how we can prevent this kind of bugs in Castle. | |
19 | - | |
17 | + First, let’s explain the Heisenbug, before we give an example. Then we analyze it, show how to improve the code, and | |
18 | + finally formulate a *requirement* to prevent & detect this kind of bug in Castle. | |
20 | 19 | |
21 | 20 | What is a Heisenbug? |
22 | 21 | ==================== |
23 | 22 | |
24 | 23 | The `heisenbug <https://en.wikipedia.org/wiki/Heisenbug>`__ is named to Werner Heisenberg, who described the *“observer |
25 | 24 | effect”*: when you look closely, the behavior changes. The same can happen to software (bugs). The behavior apparently |
26 | -changes when you study -or slightly adjust- that code. Often this is due (small) changes in timing; possibly even in | |
27 | -generated code. Therefore old (oldfashioned), sequential code on slow CPU’s is less likely to have heisenbugs then | |
25 | +changes when you study -or slightly adjust- that code. Often this is due to (small) changes in timing; possibly even in | |
26 | +generated code. Therefore old (old-fashioned), sequential code on slow CPUs is less likely to have heisenbugs than | |
28 | 27 | concurrent code on fast multi-core systems. It’s also common in threaded programs. |
29 | 28 | |
30 | 29 | .. include:: ./Heisenbug-sidebar-Sequence.irst |
@@ -32,66 +31,66 @@ | ||
32 | 31 | The sieve goes wrong |
33 | 32 | ==================== |
34 | 33 | |
35 | -Also my standard example ‘:ref:`Castle-TheSieve`’ can suffer from this issue. The initial version did work for years, | |
36 | -but failed horrible when another “machinery” was used. After studying this, the bug is simple, and easy to fix. | |
34 | +My standard example, ‘:ref:`Castle-TheSieve`’, suffered from this issue. The initial version did work for years, | |
35 | +but failed horribly when another “machinery” was used. After studying this, the bug is simple and easy to fix. | |
37 | 36 | |
38 | 37 | There are two related timing issues, that (probably only together) result in the Heisenbug. First, we introduce them one |
39 | -by one, and then show how the combination may fail. | |
38 | +by one and then show how the combination may fail. | |
40 | 39 | |
41 | 40 | |
42 | 41 | Event-order |
43 | 42 | ----------- |
44 | 43 | |
45 | -Conceptually, the `Generator` sends (events with) integers to `Sieve(2)`, which may be forward to `Sieve(3)`, then to | |
44 | +Conceptually, the `Generator` sends (events with) integers to `Sieve(2)`, which may be forwarded to `Sieve(3)`, then to | |
46 | 45 | `Sieve(5)`, etc. As shown in the **Conceptual sidebar**, we probably like to assume that each integer is fully sieved |
47 | -before the next int *starts*. This is the classic “sequential view”, we are used too. | |
46 | +before the next *’int’* *starts*. This is the classic “sequential view”, we are used to. | |
48 | 47 | |
49 | -However, that isn’t how it works. In Castle the order of events on a connection is defined (*one by one, | |
50 | -sequential*). And given the code, the integer sent by an Sieve comes always later then the incoming one. But that is all | |
51 | -we may assume. | |
48 | +However, that isn’t how it works. In Castle, the order of events on a connection is defined (*one by one, | |
49 | +sequential*). And given the code, the integer sent by a `Sieve` comes always later than the incoming one. That is all we | |
50 | +may assume. | |
52 | 51 | |BR| |
53 | -The timing of unrelated events on multiple connections is not defined. That order may depends on :ref:`TheMachinery` and | |
54 | -many other factors. Do not, as developer, assume any order -- like I did! | |
52 | +The timing of unrelated events on multiple connections is not defined. That order may depend on :ref:`TheMachinery` and | |
53 | +many other factors. Do not as a developer, assume any order --as I did! | |
55 | 54 | |
56 | -As shown in the **One-by-One sidebar** diagram, this can result that the Generator output all events first. Next, Sieve(2) | |
57 | -takes-out the even integers, then the Sieve(3) process all its input, then Sieve(5), ect. | |
55 | +As shown in the **One-by-One sidebar** diagram, this can result that the Generator is outputting all events first. Next, | |
56 | +Sieve(2) filters out the even integers, then Sieve(3) processes all its input, then Sieve(5), etc. | |
58 | 57 | |BR| |
59 | -Although we aren’t using concurrency, and it needs hug buffers -- especially when finding big primes-- it does | |
58 | +Although we aren’t using concurrency, and it needs huge buffers -- especially when finding big primes-- it does | |
60 | 59 | conceptually work. And so, it is an allowed execution [#ButImprove]_. |
61 | 60 | |
62 | 61 | |
63 | 62 | Reconnecting |
64 | 63 | ------------ |
65 | 64 | |
66 | -The chain of `Sieve`\s will grow as we find more primes. When an *int* isn’t filtered-out and so reaches the `Finder` a | |
67 | -*new prime* is found. Then, a new Sieve-component is created and inserted to the chain. | |
65 | +The chain of `Sieve`\s will grow as we find more primes. When an *int* isn’t filtered-out and so reaches the `Finder` a | |
66 | +*new prime* is found. Then, a new Sieve component is created and inserted into the chain. | |
68 | 67 | |BR| |
69 | -This is done by the Main-component (which is signaled by the Finder) [#orVariant]_. | |
68 | +This is done by the Main component (which is signaled by the Finder) [#orVariant]_. | |
70 | 69 | |
71 | -Therefore, `Main` remembers the ``last_sieve`` and reconnects it’s output to the newly creates `Sieve`. And temporally | |
72 | -connects that new-Sieve’s output to the Finder. For every newly found prime this repeats. | |
70 | +Therefore, `Main` remembers the ``last_sieve`` and reconnects its output to the newly creates `Sieve`. And temporally | |
71 | +connects that new-Sieve’s output to the Finder. For every newly found prime, this repeats. | |
73 | 72 | |BR| |
74 | -This detail is shown in the **With Details** sidebar diagram; where the `Finder` and `Main` component, and all message’s | |
73 | +This detail is shown in the **With Details** sidebar diagram; where the `Finder` and `Main` component and all messages | |
75 | 74 | to/from them are shown. |
76 | 75 | |
77 | 76 | Assuming the initial “conceptual” order, you will see the same Sieve(s) become alive (“new” message), and are added to |
78 | -the end of the sieve-chain. The integers still flow (now, shown as “try(`int`)” messages) by this sieve. | |
77 | +the end of the sieve chain. The integers still flow (now, shown as “try(`int`)” messages) by this sieve. | |
79 | 78 | |BR| |
80 | -You will aslo notice the `Finder` does indeed find all primes. | |
79 | +You will also notice the `Finder` does indeed find all primes. | |
81 | 80 | |
82 | 81 | |
83 | 82 | The combination |
84 | 83 | =============== |
85 | 84 | |
86 | -Now lets study how the sieve-chain will grow with a “fast generator”, and the one-by-one order of events is used. This | |
85 | +Now let us study how the sieve chain will grow with a “fast generator”, and the one-by-one order of events is used. This | |
87 | 86 | diagram is shown below. |
88 | 87 | |
89 | -As we can see in the picture, it goes horrible wrong. No proper sieve is created, and we will find intergers as *4* and | |
90 | -*6* as prime -- clearly this is wrong. | |
88 | +As we can see in the picture, it goes dreadfully wrong. No proper chain is created, and we will find integers like **4** | |
89 | +and **6**. This is wrong, they are not prime. | |
91 | 90 | |BR| |
92 | -With a (very) *fast Generator*, **all** intergers are send to the `Finder` --before any `Sieve` is created, and so any | |
93 | -int is reported as prime. And, as for each found “prime” a Sieve-component is created, to many elements are added to the | |
94 | -chain. On top of that, no integer is ever sieved.... | |
91 | +With a (very) *fast Generator*, **all** integers are sent to the `Finder` --before any `Sieve` is created, and so any | |
92 | +int is reported as prime. And, too many elements are added to the chain, as a Sieve component is created for each found | |
93 | +“prime”. On top of that, no integer is ever sieved... | |
95 | 94 | |
96 | 95 | This is just **an** example. As we can’t predict (or assume) any order, we can find other results too. And, when we add |
97 | 96 | “debugging print statement” (and *look closer*), we change the timing and will find other results. We found the |
@@ -104,10 +103,11 @@ | ||
104 | 103 | |
105 | 104 | It is not *“the timing”* that is wrong! |
106 | 105 | |BR| |
107 | - A concurent programm is only correct when it goes right for **any** possible timing. | |
106 | + A concurrent program is only correct when it goes right for **any** possible timing. | |
108 | 107 | |
109 | - As in all SW-engineering, we can prove it is buggy when it goes wrong at least once. That is what we have shown. And | |
110 | - so, the original code is *wrong*. | |
108 | + As in all software engineering, we can prove it is buggy when it goes wrong at least once. That is what we have | |
109 | + shown. And so, the original code is *wrong*. | |
110 | + | |
111 | 111 | |
112 | 112 | How to improve? |
113 | 113 | =============== |
@@ -1,6 +1,6 @@ | ||
1 | 1 | @startuml |
2 | 2 | 'hide footbox |
3 | -title Wrong (Generate faster & slow Creation) | |
3 | +title Wrong (Generate fast & Slow creation) | |
4 | 4 | |
5 | 5 | participant Main as M |
6 | 6 | participant Finder as F |
@@ -51,26 +51,26 @@ | ||
51 | 51 | |
52 | 52 | M -[#red]\ S4: new |
53 | 53 | activate S4 |
54 | -note right: Another flaw: We shouldn't a **Sieve(4)** | |
54 | +note right: Another flaw: We shouldn't have **Sieve(4)** | |
55 | 55 | S3 \\--o M: "reconneced to Sieve(4)" |
56 | 56 | |
57 | 57 | |
58 | 58 | M -[#purple]\ S5: new |
59 | 59 | activate S5 |
60 | 60 | hnote right: Sieve(5) never gets input; or at least to little |
61 | -S4 \\--o M: "reconneced to Sieve(4)" | |
61 | +S4 \\--o M: "reconneced to Sieve(5)" | |
62 | 62 | |
63 | 63 | |
64 | 64 | M -[#red]\ S6: new |
65 | 65 | activate S6 |
66 | -note right: Another flaw: We shouldn't a **Sieve(6)** | |
66 | +note right: Another flaw: We shouldn't have **Sieve(6)** | |
67 | 67 | S5 \\--o M: "reconneced to Sieve(6)" |
68 | 68 | |
69 | 69 | |
70 | 70 | M -[#red]\ S7: new |
71 | 71 | activate S7 |
72 | 72 | S6 \\--o M: "reconneced to Sieve(6)" |
73 | - | |
73 | +hnote right: Again: too late | |
74 | 74 | |
75 | 75 | == etc == |
76 | 76 |