<div dir="ltr">Hi Daniel,<div><br></div><div>Thank you very much for this complete answer. It is extremely helpful and very much appreciated!</div><div><br></div><div>I've updated go-geos to only lock the first context in <a href="https://github.com/twpayne/go-geos/commit/05d44c727f8d75b5bb420a557b956a52de8fd538">https://github.com/twpayne/go-geos/commit/05d44c727f8d75b5bb420a557b956a52de8fd538</a>.</div><div><br></div><div>Best regards,</div><div>Tom</div><div><br></div><div><br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Thu, 9 Jan 2025 at 14:41, Daniel Baston <<a href="mailto:dbaston@gmail.com" target="_blank">dbaston@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div dir="ltr"><div>Hi Tom,</div><div><br></div><div>With
 the caveat that I did not develop the "_r" API, here's my 
understanding. The implementation of GEOS contexts is quite short and 
can be found near the top of geos_ts_c.cpp. From looking at the 
implementation we can see that a context has pretty limited 
functionality:</div><div><br></div><div>- registering error and notice handlers<br></div><div>- passing error messages to these handlers using a context-local buffer<br></div><div>- storing properties of a global WKB writer</div><div>-
 storing a single point that is mutated by a small set of functions such
 as GEOSPreparedContainsXY that want to avoid instantiating a point on 
each call<br></div><div><br></div><div>The "_r" functions are a C API 
construct only and do not indicate anything about the thread-safety of 
the underlying algorithms. The parts of GEOS that actually _do_ things 
are implemented in C++ and have no notion of a "context." </div><div><br></div><div>With regard to the situation you describe in your email:</div><div><br></div><div>GEOSUnion_r(g.context.handle, g.geom, other.geom)</div><div><br></div><div>GEOSUnion_r
 only accesses a context for the purpose of error reporting, and the 
only context it will access is the first argument. It does not matter 
whether "g" or "other" were created using this context, or whether "g" 
and "other" have the same context. If you were to call GEOSUnion_r 
simultaneously from multiple threads using the same first argument, 
AFAIK the worst thing that could happen is that simultaneously-emitted 
error messages generated would be garbled. But you can prevent that by 
locking the context provided in the first argument.<br></div><div><br></div><div>If
 the context of "g" and "other" is irrelevant, can you use them safely 
from multiple threads? Not necessarily. The programming style used in 
GEOS lends itself to thread-safety (objects are generally immutable, 
etc.) although the use of lazy initialization sometimes gets in the way 
and the thread-safety of various operations is not well-documented or 
tested. The pattern you describe of locking the contexts of both "g" and
 "other" will protect against thread-safety issues but is unnecessarily broad; the same benefit
could be achieved with locks scoped to the geometries themselves. Even this more limited locking is unnecessary for many operations, but unfortunately the GEOS docs don't give any guidance about which ones. Adding more tests and documentation around GEOS thread-safety would be a big improvement for the library.<br></div><div></div><div><br></div><div></div><div>Dan<br></div><div><br></div><div>PS<br></div><div><br></div><div>There has been some discussion about giving contexts additional responsibility, such as managing interrupts:</div><div><a href="https://github.com/libgeos/geos/pull/803" target="_blank">https://github.com/libgeos/geos/pull/803</a></div><div><br></div><div>You might also find some useful discussion within the georust project, e.g.</div><div><a href="https://github.com/georust/geos/pull/164" target="_blank">https://github.com/georust/geos/pull/164</a></div></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Wed, Jan 8, 2025 at 9:36 PM Tom Payne <<a href="mailto:twpayne@gmail.com" target="_blank">twpayne@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr">Gentle ping on this. I would really like to understand the requirements for combining geometries from different GEOS contexts.<div><br></div><div>If my question is unclear, missing information, stupid, answered elsewhere, or there is any other problem with it then please tell me. I would really like to understand this.</div><div><br></div><div>Regards,</div><div>Tom</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Fri, 22 Nov 2024 at 10:50, Tom Payne <<a href="mailto:twpayne@gmail.com" target="_blank">twpayne@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr">Hi,<div><br></div><div>tl;dr when calling a function which takes multiple geometries, like <a href="https://libgeos.org/doxygen/geos__c_8h.html#afd3d82a6d039ab5637e0a8a066694b7d" target="_blank">GEOSUnion_r</a>, where the two geometries are associated with different contexts, do I have to ensure that both geometries' contexts are used exclusively?</div><div><br></div><div>Background:</div><div><br></div><div>I maintain the <a href="https://github.com/twpayne/go-geos" target="_blank">Go bindings for GEOS</a>, which exclusively use the thread-safe *_r functions. Every created geometry is associated with a context. Every context has a mutex to ensure that it is only accessed from a single thread at time.</div><div><br></div><div>For functions that take multiple geometries I check if the geometries are from different contexts, and if so, lock both mutexes. <a href="https://github.com/twpayne/go-geos/blob/c9ed31526fa2ee3599ffe0fdf4556a6cf9c0b204/geommethods.go#L865-L875" target="_blank">Here</a> is an example:<br><br>    // Union returns the union of g and other.<br>    func (g *Geom) Union(other *Geom) *Geom {<br>        g.mustNotBeDestroyed()<br>        g.context.Lock()<br>        defer g.context.Unlock()<br>        if other.context != g.context {<br>            other.context.Lock()<br>            defer other.context.Unlock()</div><div>        }<br>        return g.context.newGeom(C.GEOSUnion_r(g.context.handle, g.geom, other.geom), nil)<br>    }<br><br></div><div>However, there is a potential deadlock if there are two geometries A and B owned by different contexts and A.Union(B) and B.Union(A) are called simultaneously from different threads. In practice this pattern is unlikely to occur, but I would like to guard against it.<br><br>I checked the <a href="https://libgeos.org/usage/c_api/" target="_blank">documentation on GEOS's C API</a>, the <a href="https://github.com/libgeos/geos/blob/main/DEVELOPER-NOTES.md" target="_blank">GEOS developer notes</a>, did a superficial <a href="https://github.com/search?q=repo%3Alibgeos%2Fgeos+context&type=issues" target="_blank">search of the GitHub issues</a>, and a superficial <a href="https://www.google.com/search?q=site%3Alists.osgeo.org+%22%5Bgeos-devel%5D%22+context" target="_blank">search of the geos-devel</a> archives, and could not find an answer to this question.<br><br>Many thanks for any insight,</div><div>Tom</div></div>
</blockquote></div>
</blockquote></div>
</blockquote></div>