{"id":1806,"date":"2023-11-29T16:47:44","date_gmt":"2023-11-29T15:47:44","guid":{"rendered":"http:\/\/oopm.org\/?page_id=1806"},"modified":"2024-12-13T11:31:02","modified_gmt":"2024-12-13T10:31:02","slug":"8-3-other-issues","status":"publish","type":"page","link":"https:\/\/oopm.org\/?page_id=1806","title":{"rendered":"13.3 Other issues in parallel programming"},"content":{"rendered":"<div class=\"pdfprnt-buttons pdfprnt-buttons-page pdfprnt-top-right\"><a href=\"https:\/\/oopm.org\/index.php?rest_route=wpv2pages1806&print=pdf\" class=\"pdfprnt-button pdfprnt-button-pdf\" target=\"_blank\"><img decoding=\"async\" src=\"https:\/\/oopm.org\/wp-content\/plugins\/pdf-print\/images\/pdf.png\" alt=\"image_pdf\" title=\"View PDF\" \/><\/a><a href=\"https:\/\/oopm.org\/index.php?rest_route=wpv2pages1806&print=print\" class=\"pdfprnt-button pdfprnt-button-print\" target=\"_blank\"><img decoding=\"async\" src=\"https:\/\/oopm.org\/wp-content\/plugins\/pdf-print\/images\/print.png\" alt=\"image_print\" title=\"Print Content\" \/><\/a><\/div>\n<p class=\"wp-block-paragraph\">As mentioned, parallel programming is a complicated and tricky endeavour and there are many pitfalls in practicing parallel programming. This is due to the fact that a complex system with many parallel activities is difficult to grasp and the state-of-art of methods, techniques and programming languages is not that well developed. We strongly advise readers of the book to take one or more courses in parallel programming before starting to practice it. Below we touch upon some of the issue of parallel programming, which may be taken as a starting point.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Critical regions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">One common problem with parallel programming is to avoid that two or more parallel objects access the same <em>resources<\/em> at the same time since this may lead to an undefined result. A resource in this sense may be data stored in other objects often referred to as <em>shared objects<\/em>. It may also be external devices like the console object that prints strings in a window on the screen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The statements of a parallel object accessing shared objects is often referred to as a <em>critical region<\/em> or <em>critical section<\/em>. The situation where two or more parallel objects access the same shared objects implying an undefined result is referred to as a <em>race condition<\/em> as introduced in section <script>mkRef(\"A simple search system\")<\/script>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The term <em>mutual exclusion<\/em> is a property of parallel programming that is introduced in order to prevent race conditions. Mutual exclusion must ensure that a parallel object never enters a critical region while another parallel object is in a critical region modifying the same shared data and\/or using a shared resource like a console.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The next example shows a program with shared objects, critical regions and a situation with a race condition:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>raceSystem: <strong>obj<\/strong> BasicSystem\n   N: <strong>var<\/strong> integer   &lt;&lt;&lt; shared data\n   P1: <strong>obj<\/strong> BasicProcess\n      ...\n      N := N + 1    &lt;&lt;&lt; critical region\n      ...\n   P2: <strong>obj<\/strong> BasicProcess\n      ...\n      N := N + 2    &lt;&lt;&lt; critical region\n      ...\n   N := 10\n   P1.start\n   P2.start<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The variable <code>N<\/code> is an example of data shared between the parallel objects <code>P1<\/code> and <code>P2<\/code>. <code>P1<\/code> and <code>P2<\/code> both update the value of <code>N<\/code> and since this may happen at the same time, the resulting value of <code>N<\/code> is undefined. At the end of the program the value of <code>N<\/code> may be one of 11, 12 or 13. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This is due to the fact that execution of an assignment like <code>N := N + 1<\/code> by <code>P1<\/code> is not an atomic operation in the sense that <code>P2<\/code> may access <code>N<\/code> while <code>P1<\/code> is executing <code>N := N + 1<\/code>. Execution of <code>N := N + 1<\/code> takes place in a number of steps as shown below:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>R: <strong>var<\/strong> integer\nR := N\nR := R + 1\nN := R<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>P1<\/code> reads the value to a a variable <code>R<\/code>, which in practice may be a register of the core unit executing <code>P1<\/code>. It then increments <code>R<\/code> and stores the value of <code>R<\/code> back into <code>N<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Execution of <code>N := N + 1<\/code> takes place in a similar way and this may be lead to the following scenario:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>P1.R := N\nP2.R := N\nP2.R := P2.R + 2\nN := P2.R         &lt;&lt;&lt; now N = 12\nP1.R := P1.R + 1\nN := P1.R         &lt;&lt;&lt; P1 overrides the value of N and now N = 11<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">In this scenario the resulting value of <code>N<\/code> is 11. The above statements may be interleaved in a number of other ways leading to different results as mentioned above.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In order to avoid the race conditions, the critical regions must be protected by mutual exclusion and this might be done by declaring <code>N<\/code> within a <code>Monitor<\/code>-object and defining methods for updating <code>N<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">With respect to accessing shared objects, there is a difference between parallel objects reading the values of shared objects without modifying the shared objects, and writing new values into shared objects. It is in<mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-foreground-color\"> <\/mark>general harmless to have multiple parallel objects reading shared objects at the same time. If, however, a parallel object is writing into<mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-foreground-color\"> <\/mark>and thus modifying a shared object, the mutual exclusion mechanism should ensure that no other parallel objects are reading or writing the shared objects since this may lead to race conditions.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Parallel objects reading shared objects are often referred to as <em>readers<\/em> and those writing shared objects as <em>writers<\/em>. The problem of handling multiple readers and at most one reader is called the r<em>eaders-writers <\/em>problem.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In section 8.1, the <code>searcher<\/code>-objects store matching <code>records<\/code> during search in the <code>Set<\/code>-object <code>matches<\/code>. Here the critical sections are the statement <code>matches.insert(P)<\/code>. The object <code>matches<\/code> is an example of a shared object, and since it is encapsulated in the <code>collector<\/code>-object, which is a <code>Monitor<\/code>, mutual exclusion is guaranteed when the <code>searcher<\/code>-objects access <code>matches<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>records<\/code>-object used by the <code>searcher<\/code>-objects in the same section <s>o<\/s>is also an example of a shared object<s>s<\/s>. Here multiple <code>searcher<\/code>-objects may access <code>records<\/code> at the same time, but since they only read data and do not modify the objects, this is safe and does not lead to race conditions. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This is, however, at the risk of the programmer. The code in the example might as well modify the records without the compiler of the language complaints. In general, it is a great advantage if the language mechanisms guarantees mutual exclusion.<mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-foreground-color\"> <\/mark><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">As mention, the monitor is one language mechanism that may be used to ensure mutual exclusion. Below we will mention some of the most common mechanism used in mainstream programming languages.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">High-level synchronization<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">In a previous section, we have introduce monitors as an example of a high-level synchronisation and communication abstraction. There are many other examples of such high-level abstractions and here we will  give one more example.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A common form of communication between parallel processes is  message-passing where a process may send a message directly to another process. The form of a message varies depending on the system, library, and\/or language being used. A message may be a text, a reference to an object, or a value. For the difference between reference to an object and a value, see the Chapter Objects and Values.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Another difference between message-passing systems is whether or not the &#8216;sending of the message&#8217; is synchronous or asynchronous just as it differs what sending of message actually means. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In this book, sending of a message is defined as invocation of method of a parallel object. In general, a method invocation is <em>synchronous<\/em> in the sense that the invoker of the method is blocked (waits) until the message has been executed by the receiver whereafter it returns to the invoker.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In systems with parallel objects, synchronous method invocation may imply that a calling parallel object may wait unnecessarily for a method to be executed by the receiver. For this reason, so-called <em>asynchronous<\/em> method invocations may be used.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In asynchronous method invocations, the method object is usually inserted into a queue at the receiving object, and the receiver then executes the method objects from the queue. The exact way of how this is done varies from system to system.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here we will use as an example, a simple asynchronous message passing system where each method object is inserted in a queue at the receiver, which then executes them in the order they arrive.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We use a variant of the search example from section <script>mkRef(\"A simple search system\");<\/script> where the <code>searcher<\/code>-objects send a matching <code>Person<\/code> record directly to the <code>printer<\/code>-object and not via a <code>Monitor<\/code>-object:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>asynchSystem: <strong>obj<\/strong> SimpleAsynchSystem\n   records: <strong>obj<\/strong> IndexedRef(100,Person)\n   search(first: <strong>var<\/strong> integer, <strong>last<\/strong>: var integer):\n      current: <strong>ref<\/strong> Person\n      for (first) :to(last):repeat\n          if ((18 &lt;= records.get&#91;inx].age)\n              &amp;&amp; (records.get&#91;inx].age &lt;= 24)) :then\n             current := records.get&#91;inx]\n             inner(search)\n   Person(name: <strong>ref<\/strong> String, age: <strong>var<\/strong> integer):\n      ... \n   Searcher: Process\n      inner(Searcher)\n   searcherA: <strong>obj<\/strong> Searcher(\"SearcherA\")\n      search(1,33)\n         printer.add(current)\n   searcherB: obj Searcher(\"SearcherB\")\n      search(34,66)\n         printer.add(current)\n   searcherC: obj Searcher(\"SearcherC\")\n      search(67,100)\n         printer.add(current)      \n   printer: <strong>obj<\/strong> Process(\"Printer\")\n      add(P: <strong>ref<\/strong> Person): entry\n         console.print(\"Found: \" + P.name + \",age: \" + P.age + \"\\n\") \n   searcherA.start\n   searcherB.start\n   searcherC.start   \n   printer.start<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Note that the class <code>Process<\/code> used as a superclass in this example is not the same as the one in section <script>mkRef(\"A simple search system\");<\/script>, which is defined as a local class of <code>BasicSystem<\/code> whereas the one used here is a local class of <code>SimpleAsynchSystem<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A <code>Process<\/code>-object in this system has a queue of waiting method objects. When a <code>searcher<\/code>-object executes <code>printer.add(current)<\/code>, the <code>add<\/code>-object is inserted in the queue of the <code>printer<\/code>. The <code>printer<\/code> repeatedly checks if there is a method object in the queue, and if one is found, it is executed. The checking of the queue is defined in the <code>Process<\/code> class of which <code>printer<\/code> is subclassed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">As mentioned, there are many proposals for high-level communication and synchronisation abstractions and they have many variants. This may be due to the fact that different problems require different mechanisms and no mechanisms have turned out to be dominant.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Low-level synchronization<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The monitor and asynchronous method passing are examples of a high-level synchronisation mechanisms. Most mainstream language include what we characterise as low-level synchronization mechanisms. In this section, we describe some of these.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Lock<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">One example of a is a <em>lock<\/em>. The idea is that a parallel object has to acquire a given lock before entering a critical region. In the example below, we use a lock to ensure mutual access to the global integer variable <code>N<\/code> from the above example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>raceSystem: <strong>obj<\/strong> BasicSystem\n   mutex: <strong>obj<\/strong> Lock  -- declaration of a lock\n   N: <strong>var<\/strong> integer   -- shared data\n   P1: <strong>obj<\/strong> BasicProcess\n      ...\n      mutex.wait    --- wait until the lock is free\n      N := N + 1    --- critical region\n      mutex.signal  --- signal that the lock is released\n      ...\n   P2: <strong>obj<\/strong> BasicProcess\n      ...\n      mutex.wait\n      N := N + 2    --- critical region\n      mutex.signal\n      ...\n   N := 10\n   P1.start\n   P2.start\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">We have extended the example with a lock declared by <code>mutex: <strong>obj<\/strong> Lock<\/code>. Each parallel object <code>P1<\/code>, and <code>P2<\/code>, executes a <code>mutex.wait<\/code> before incrementing <code>N<\/code>. If the <code>Lock<\/code>, <code>mutex<\/code> is free, it becomes locked and the process may enter the critical regions and modify <code>N<\/code>. After this, execution of <code>mutex.signal<\/code> releases the <code>Lock<\/code> and if some other process is waiting for the <code>Lock<\/code>, it may obtain it and enter the critical region. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If more than one process is waiting, they may obtain the <code>Lock<\/code> in the order of which they tried to acquire it or one is picked randomly. This depends on how the <code>Lock<\/code> is implemented.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Semaphore<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Another example of a low-level synchronisation mechanism is the <em>semaphore<\/em>. A semaphore has an associated integer variable. The variable is initialised with a (positive) integer value. Like a <code>Lock<\/code>, it has methods <code>wait<\/code> and <code>signal<\/code> The <code>wait<\/code>-method decrements the value and if it becomes negative, the executing process is blocked until the value becomes zero. The <code>signal<\/code> method increments the value. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">There are two variants of a semaphore, a <em>binary semaphore<\/em> and a <em>counting semaphore<\/em>. For a binary semaphore, the value may be either 0 or 1. A binary semaphore is thus similar to a lock.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A counting semaphore may be used to manage a pool of shared resources. Suppose you live in community that have 10 bicycles to be shared between people in the community. You may synchronise access to these bicycles using a counting semaphore:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>myCommunity: <strong>obj<\/strong> BasisSystem\n   bicyclePool: <strong>obj<\/strong> CountingSemaphore(10)\n   P1: <strong>obj<\/strong> BasicProcess\n       ...\n       bicyclePool.wait     -- wait for a free bicycle\n       -- take a free bicycle\n       ...\n       bicyclePool.signal   -- return the bicycle\n   P2: <strong>obj<\/strong> BasicProcess\n       ...\n      <\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>myCommunity<\/code> object has a <code>CountingSemaphore<\/code> object, <code>bicyclePool<\/code>, initialized to the value 10 representing the availability of 10 bicycles. A parallel object like <code>P1<\/code> that wants to obtain a bicycle executes the method <code>bicyclePool.wait<\/code>. For each such invocation, the associated integer is decremented by one and it if becomes zero, the process has to wait. In this way up to 10 processes can obtain a bicycle. When a process has finished using a bicycle it must return it and  execute a <code>bicyclePool.signal,<\/code> which increments the associated integer.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We leave it as an exercise to the reader to rewrite the <code>search<\/code>-example to use locks and\/or semaphores.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It is in general more safe to use a high-level synchronisation mechanism like a monitor than the above low-level mechanisms. Using locks and semaphores, a programmer may forget to invoke a <code>wait<\/code>&#8211; or <code>signal<\/code>-method, in which case race-conditions may appear. There is no way the language and compiler can guarantee that no such errors are made. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Common challenges in parallel programming<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">In this section, we mention some of the problems that often arise in parallel programming.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Deadlock<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\"><em>A<strong>&nbsp;deadlock&nbsp;<\/strong><\/em>is a situation where a set of parallel objects are blocked because each object is holding a resource and waiting for another resource acquired by some other object.&nbsp;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">As a real-life example, a deadlock may arise if two cars crossing a single-lane bridge&nbsp;from opposite directions and as long as none of the cars is willing to back, none of them can proceed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">As a programming example, we may use the bank system. If multiple clerks can make transactions on the accounts, one needs to synchronize acces to accounts. This can be done by introducing a lock for each account:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>JohnSmithsAccount: <strong>obj<\/strong> Account(\"John Smith\")\nJohnSmitLock: <strong>obj<\/strong> Lock\nlizaJonesAccount: <strong>obj<\/strong> Account(\"John Smith\")\nLizaJonesLock: <strong>obj<\/strong> Lock<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A clerk, <code>clerkA<\/code>,  may need to transfer money from <code>JohnSmithsAccount<\/code> to <code>LizaJonesAccount<\/code>, and to do this he\/she needs to acquire the locks for these accounts:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>clerkA: <strong>obj<\/strong> BasicProcess\n   JohnSmithLock.wait\n   LizaJonesLock.wait\n   transfer(500,JohnSmithsAccount, lizaJonesAccount)\n   JohnSmithLock.signal\n   LizaJonesLock.signal<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Another clerk, <code>clerkB<\/code>, may decide to transfer money from <code>LizaJonesAccount<\/code> to <code>JohnSmithsAccount<\/code> and thus acquire the locks:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>clerkB: <strong>obj<\/strong> BasicProcess\n   LizaJonesLock.wait\n   JohnSmithLock.wait\n   transfer(500,lizaJonesAccount,JohnSmithsAccount)\n   LizaJonesLock.signal\n   JohnSmithLock.signal<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A situation may thus arise where <code>clerkA<\/code> has acquired JohnSmithLock and is waiting for <code>LizaJonesLock<\/code> while at the same time, <code>clerkB<\/code> has acquired <code>LizaJonesLock<\/code> and is waiting for <code>JohnSmithLock<\/code>. The implication of this is that the two clerks are waiting for each other to release a lock, but this cannot happen.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Starvation<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Starvation or resource starvation is a problem encountered where a parallel object is constantly denied the necessary resource to carry out its work. Starvation can be caused by errors in the algorithms scheduling the parallel objects and\/or the synchronisation mechanism like lock, semaphore, monitor, etc.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Termination<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">For an algorithm in general, it is an important property that it can be validated that the algorithm actually terminates &#8211; i.e. finishes its computation. For sequential algorithms, this can be more or less difficult. For parallel algorithms it may be even more difficult since it may involve a more or less complicated protocol where the various parallel objects involved in the algorithm communicate to each other that the algorithm should terminate.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Overhead <\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">A system of parallel objects involves communication and synchronization between the objects and this gives an overhead compared to the core of the algorithm being computed. It is therefore necessary to be aware of the possible overhead when designing parallel systems.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Further reading\/courses<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">We have previously said that in order to acquire the necessary skills to become a software developer, the reader must take a course in algorithms and data structures. To be able to develop parallel systems it is also necessary to take a course on parallel algorithms and data structures. This includes techniques to handle the above mentioned problems with deadlock, starvation, termination and overhead. <\/p>\n<div style=\"display:flex; gap:10px;justify-content:center\" class=\"wps-pgfw-pdf-generate-icon__wrapper-frontend\">\n\t\t<a  href=\"https:\/\/oopm.org?action=genpdf&amp;id=1806\" class=\"pgfw-single-pdf-download-button\" ><img src=\"https:\/\/oopm.org\/wp-content\/plugins\/pdf-generator-for-wp\/admin\/src\/images\/PDF_Tray.svg\" title=\"Generate PDF\" style=\"width:auto; height:45px;\"><\/a>\n\t\t<\/div>","protected":false},"excerpt":{"rendered":"<p>As mentioned, parallel programming is a complicated and tricky endeavour and there are many pitfalls in practicing parallel programming. This is due to the fact that a complex system with many parallel activities is difficult to grasp and the state-of-art of methods, techniques and programming languages is not that well developed. We strongly advise readers [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":1669,"menu_order":3,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-1806","page","type-page","status-publish","hentry"],"mb":[],"mfb_rest_fields":["title","gutenberg_elementor_mode"],"_links":{"self":[{"href":"https:\/\/oopm.org\/index.php?rest_route=\/wp\/v2\/pages\/1806","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/oopm.org\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/oopm.org\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/oopm.org\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/oopm.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1806"}],"version-history":[{"count":151,"href":"https:\/\/oopm.org\/index.php?rest_route=\/wp\/v2\/pages\/1806\/revisions"}],"predecessor-version":[{"id":10324,"href":"https:\/\/oopm.org\/index.php?rest_route=\/wp\/v2\/pages\/1806\/revisions\/10324"}],"up":[{"embeddable":true,"href":"https:\/\/oopm.org\/index.php?rest_route=\/wp\/v2\/pages\/1669"}],"wp:attachment":[{"href":"https:\/\/oopm.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1806"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}