<br><br><div class="gmail_quote">On Tue, Feb 19, 2008 at 3:32 AM, Behdad Esfahbod &lt;<a href="mailto:behdad@behdad.org">behdad@behdad.org</a>&gt; wrote:<br><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
On Sat, 2008-02-16 at 00:50 +0200, Iosif Haidu wrote:<br>&gt;&gt; Hi,<br>
<div class="Ih2E3d"><br>
&gt;Hi,<br>
<br>
&gt;&gt; I have recently experienced an interesting behavior of the<br>&gt;&gt; &#39;cairo_restore()&#39; function. It doesn&#39;t restore the cairo context saved<br>
&gt;&gt; by &#39;cairo_save()&#39; if an error occurs when executing windows gdi<br>
&gt;&gt; functions.<br>
&gt;&gt;<br>
&gt;&gt; When I used these two functions I assumed that they must provide a<br>
&gt;&gt; transaction like functionality:<br>
<br>
</div>&gt; Wrong assumption. &nbsp;And of course wrong documentation on our side. &nbsp;What<br>
&gt; cairo_save/restore do is save/restore all the settings/parameters of the<br>
&gt; context except for groups, current path, and of course status. &nbsp;In<br>
&gt; postscript speak, it saves/restores the graphics state (sans the path).<br>
<div class="Ih2E3d"></div></blockquote><div><br>I agree with you that my assumption might be wrong, but that&#39;s what happens when function names are not chose wisely (according with their implementation). If the &#39;cairo_restore()&#39; function would have been named &#39;cairo_partial_restore()&#39; instead, then I would have know that probably only some of the fields of &#39;cairo_t&#39; structure will be restored and not all of them.<br>
Do you consider that windows device context handle is not worth to be restored ? (&#39;dc&#39; member of &#39;cairo_win32_surface_t&#39;)<br><br></div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
<div><br>
<br>&gt;&gt; &nbsp;&#39;cairo_save()&#39; saves the cairo context while &#39;cairo_restore()&#39; would<br>
&gt;&gt; restore it even if an error would occur by executing cairo functions<br>
&gt;&gt; between. But this is not the case. If a cairo function fail to execute<br>
&gt;&gt; between, &nbsp;by failing one of the windows gdi function, then<br>
&gt;&gt; &#39;cairo_restore()&#39; will not restore the previously saved context.<br>
&gt;&gt; I have experienced such situation for the application I&#39;m working on<br>
&gt;&gt; it. For an unknown reason sometime some windows gdi functions<br>
&gt;&gt; (CreateCompatibleDC(), BitBlt() and other) fail to execute with error<br>
&gt;&gt; code 6 and 8. In this situation the cairo function responsible with<br>
&gt;&gt; that windows gdi function call will set the error status to<br>
&gt;&gt; CAIRO_STATUS_NO_MEMORY and the &#39;cairo_restore()&#39; will not restore the<br>
&gt;&gt; cairo context. I have noticed that cairo context actually has not been<br>
&gt;&gt; damaged by the failure of the windows gdi function, yet the context is<br>
&gt;&gt; not restored.&nbsp;</div></blockquote><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;"><div class="Ih2E3d"><br>
</div>&gt; As I said on the bug, the this is a bug that should be fixed. &nbsp;If the<br>
&gt; failing gdi functions are not fatal, this should be handled in cairo.<br>
<br>
&gt; If the errors are fatal errors (say, real out-of-memory), cairo fails to<br>
&gt; produce the exact output that you asked it to, and that&#39;s an error. &nbsp;And<br>
&gt; cairo&#39;s error handling model is to retain errors. &nbsp;It&#39;s really that<br>
&gt; simple.&nbsp; <br>
<div class="Ih2E3d"></div></blockquote><div><br>Well, the output of the error is that my application main window freezes and nothing is drawn anymore (I must close the application and start again). I would say that this error it&#39;s a critical one.<br>
The problem why the main screen freezes is because the windows device context handle is not restored and somehow inside the library this is damaged. For an application that draws real time waveforms it would be acceptable to lose some drawings but not to freeze.<br>
Probably you didn&#39;t try to check the cairo library functions and try some scenario of &#39;what can happen if xxx windows gdi function fails for some unknown reason&#39;. But I can give you some examples:<br><br>a)<br>
File: cairo-win32-surface.c<br>Function: static cairo_status_t _create_dc_and_bitmap(...)<br>...<br>surface-&gt;dc = CreateCompatibleDC(original_dc);<br>if (!surface-&gt;dc)<br>goto FAIL;<br><br>surface-&gt;bitmap = CreateDIBSection (surface-&gt;dc,<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bitmap_info,<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DIB_RGB_COLORS,<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;bits,<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NULL, 0);<br>if (!surface-&gt;bitmap)<br>goto FAIL;<br>...<br>
surface-&gt;saved_dc_bitmap = SelectObject (surface-&gt;dc,<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; surface-&gt;bitmap);<br>if (!surface-&gt;saved_dc_bitmap)<br>goto FAIL;<br>...<br>FAIL:<br>&nbsp;&nbsp;&nbsp; status = _cairo_win32_print_gdi_error (&quot;_create_dc_and_bitmap&quot;);<br>
&nbsp;&nbsp;&nbsp; ...<br>&nbsp;&nbsp;&nbsp; if (surface-&gt;saved_dc_bitmap) {<br>&nbsp;&nbsp;&nbsp; SelectObject (surface-&gt;dc, surface-&gt;saved_dc_bitmap);<br>&nbsp;&nbsp;&nbsp; surface-&gt;saved_dc_bitmap = NULL;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; <br>In my situation, for unknown reason, the execution of &#39;CreateCompatibleDC()&#39; fails. I&#39;ll let you to find out what happens in the FAIL section if &#39;CreateCompatibleDC()&#39; or &#39;CreateDIBSection()&#39; fails.<br>
<br>b)<br>File: cairo-win32-surface.c<br>Function: static cairo_status_t _cairo_win32_surface_acquire_dest_image()<br>...<br>if (GetClipBox (surface-&gt;dc, &amp;clip_box) == ERROR)<br>return _cairo_win32_print_gdi_error (&quot;_cairo_win3_surface_acquire_dest_image&quot;);<br>
...<br><br>The execution of &#39;GetClipBox()&#39; sometimes fails.<br><br>c)<br>File: cairo-win32-surface.c<br>Function: static cairo_int_status_t _cairo_win32_surface_set_clip_region()<br>...<br>if (ExtSelectClipRgn (surface-&gt;dc, gdi_region, RGN_AND) == ERROR)<br>
status = _cairo_win32_print_gdi_error (&quot;_cairo_win32_surface_set_clip_region&quot;);<br>..<br><br>The execution of &#39;ExtSelectClipRgn()&#39; sometimes fails.<br><br>And here are the cairo api leading to this situation:<br>
<br>&nbsp;&nbsp;&nbsp; cairo_save(pDcM);<br>&nbsp;&nbsp;&nbsp; cairo_move_to(pDcM, rBeginP.getX(), rBeginP.getY());<br>&nbsp;&nbsp;&nbsp; cairo_line_to(pDcM, rEndP.getX(), rEndP.getY());<br>&nbsp;&nbsp;&nbsp; cairo_stroke(pDcM);<br>&nbsp;&nbsp;&nbsp; cairo_restore(pDcM);<br><br><br>Now, if in the first case it&#39;s clearly a bug to not check in the FAIL section if &#39;surface-&gt;saved_dc_bitmap&#39; has a valid value (it has a garbage value != 0 and it goes inside the &#39;if&#39; and that garbage value is selected in the surface device context) for the other two cases I&#39;ll let you to find out the real reason.<br>
You could say that in the first case the bug can be fixed, so probably the problem will be also fixed. But fixing the &#39;cairo_restore()&#39; to really restore what have been saved has also other benefits. For example such bugs will not affect anymore the main application (there are other mechanisms to catch such bugs if this is the intention of retaining the error code), the execution of a cairo_save/cairo_restore block will act like a transaction (transactions are a good mechanism to build a stable systems)<br>
<br><br>&gt; What is it good for if cairo tells you that everything is ok<br>
&gt; but in reality it failed to draw some of the stuff you asked it to?<br><br>I never said that. I think you misunderstood my explanation or didn&#39;t pay attention enough on my previous e-mail. <br>The developer is able to check every moment, inside of a &#39;cairo_save()/cairo_restore()&#39; block if an error has occurred by calling &#39;cairo_status()&#39; api function and act correspondingly.&nbsp; What I have said was that is not necessary to keep the error but rather clear it after &#39;cairo_restore()&#39; will finish it&#39;s execution. The only drawback of this fix is compatibility with previous versions. And yes, there might be applications that would rather like to miss some drawings but instead restore the right device context handle.<br>
<br><br></div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;"><div class="Ih2E3d"><br>
<br>&gt;&gt; Also the cairo documentation doesn&#39;t mention that &#39;cairo_restore()&#39;<br>
&gt;&gt; will not restore the context if an error occurs in a cairo function<br>
&gt;&gt; between &#39;cairo_save()&#39; and &#39;cairo_restore()&#39;.<br>
<br>
</div>&gt; When a cairo object is in error, it shuts down. &nbsp;No further operation on<br>
&gt; it works normally. &nbsp;This is to ensure that no malicious behavior is<br>
&gt; caused by an erroneous object.</blockquote><div><br>If you will continue to follow the code execution of those examples I gave, you&#39;ll see that actually the fields of cairo context are not damaged or set to an inconsistent values. Just follow the execution path.<br>
</div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;"><br>
<div class="Ih2E3d"><br>
<br>&gt;&gt; I would like to propose a change in the &#39;cairo_restore()&#39;<br>&gt;&gt; implementation, such that:<br>
&gt;&gt; &nbsp; a) will restore the previously saved context by &#39;cairo_save()&#39; even<br>
&gt;&gt; if in an error occurs<br>
&gt;&gt; &nbsp; b) will return as result the error code generated by any cairo<br>
&gt;&gt; function executed between &#39;cairo_save()&#39; and &#39;cairo_restore()&#39;<br>
&gt;&gt; &nbsp; c) will reset the error status to CAIRO_STATUS_SUCCESS so that any<br>
&gt;&gt; next execution of &#39;cairo_save()&#39; and &#39;cairo_restore()&#39; will not exit<br>
&gt;&gt; because of a previous status set to an error code.<br>
<br>
</div>&gt; So, no. &nbsp;This doesn&#39;t make any sense to me. &nbsp;What does make some sense<br>
&gt; is to add some new api, for example cairo_clear_status(). &nbsp;But<br>
&gt; implementing that kind of API is almost impossible without really<br>
&gt; drastic changes to cairo internals.<br>
<br>
&gt; I do see where you are coming from though. &nbsp;I was myself very surprised<br>
&gt; and puzzled by the fact that this can put the cairo_t in a<br>
&gt; non-recoverable error state:<br>
<br>&gt;&nbsp; cairo_save (cr);<br>
 &gt;&nbsp; cairo_scale (cr, 0, 0);<br>
 &gt;&nbsp; cairo_restore (cr);<br>
<br>&gt; But my solution to it is not what suggest. &nbsp;My proposed solution is that<br>
&gt; errors should not be generated until there&#39;s really no other choice. &nbsp;In<br>
&gt; the above case, there&#39;s nothing wrong with the code semantically. &nbsp;For<br>
&gt; one reason, it&#39;s not drawing anything with the degenerate matrix, so it<br>
&gt; can&#39;t be an error. &nbsp;Moreover, drawing with a degenerate matrix is quite<br>&gt; well-defined and useful too. &nbsp;So, these cases are false positives in<br>
&gt; current cairo implementation that should be fixed, instead of hacked<br>
&gt; around. &nbsp;From what you described, your case is similar IMO.</blockquote><div><br>Well I disagree with your solution&nbsp; for the following reason:<br>&nbsp; - it&#39;s not easy to implement it<br>&nbsp; - it&#39;s not wise to let the developer to worry when it needs to reset the error code or not. Another thing to worry about. <br>
</div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;"><br>
<div class="Ih2E3d"><br>
<br>&gt;&gt; The function would be something like:<br>
&gt;&gt;<br>
&gt;&gt; int<br>
&gt;&gt; cairo_restore (cairo_t *cr)<br>
&gt;&gt; {<br>
&gt;&gt; ...<br>
&gt;&gt; int result = CAIRO_STATUS_SUCESS;<br>
&gt;&gt; if (cr-&gt;status != CAIRO_STATUS_SUCCESS)<br>
&gt;&gt; {<br>
&gt;&gt; &nbsp; &nbsp; result = cr-&gt;status;<br>
&gt;&gt; &nbsp; &nbsp; cr-&gt;status = CAIRO_STATUS_SUCCESS;<br>
&gt;&gt; &nbsp; &nbsp; if ((cr-&gt;gstate) &amp;&amp; (cr-&gt;gstate-&gt;target))<br>
&gt;&gt; &nbsp; &nbsp; {<br>
&gt;&gt; &nbsp; &nbsp; &nbsp; &nbsp; cr-&gt;gstate-&gt;target-&gt;status = CAIRO_STATUS_SUCCESS;<br>
<br>
</div>&gt; Any and all of cr, cr-&gt;gstate, and cr-&gt;gstate-&gt;target may be in an<br>&gt; inconsistent state at this point. &nbsp;Reseting their status to SUCCESS<br>
&gt; doesn&#39;t magically make them continue working.<br>
<div><div></div><div class="Wj3C7c"></div></div></blockquote><div><br>Actually, yes. Resetting their status to SUCCESS will magically make them continue working, because nothing is damaged in the cairo context structure if a windows gdi function fails.<br>
</div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;"><div><div class="Wj3C7c"><br>
<br>
&gt; &nbsp; &nbsp; }<br>
&gt; }<br>
&gt; // if (cr-&gt;status)<br>
&gt; // return;<br>
&gt; ...<br>
&gt; return result;<br>
&gt; }<br>
&gt;<br>
&gt;<br>
&gt; Best regards,<br>
&gt; Iosif Haidu<br>
<br>
</div></div><font color="#888888">--<br>
behdad<br>
<a href="http://behdad.org/" target="_blank">http://behdad.org/</a><br>
<br>
&quot;Those who would give up Essential Liberty to purchase a little<br>
&nbsp;Temporary Safety, deserve neither Liberty nor Safety.&quot;<br>
 &nbsp; &nbsp; &nbsp; &nbsp;-- Benjamin Franklin, 1759<br>
<br>
</font></blockquote></div><br><br>Best regards,<br>Iosif Haidu<br>