Bug 59927

Summary: make qt4 frontend thread-safe
Product: poppler Reporter: Thomas Freitag <Thomas.Freitag>
Component: qt4 frontendAssignee: poppler-bugs <poppler-bugs>
Status: RESOLVED FIXED QA Contact:
Severity: normal    
Priority: medium CC: adam.reichold
Version: unspecified   
Hardware: Other   
OS: All   
Whiteboard:
i915 platform: i915 features:
Bug Depends on:    
Bug Blocks: 59933    
Attachments: thread-safe qt4 frontend
okular testcase
test case for multi-threaded access
test case for multi-threaded document access
fix font info stuff, share annot over threads
move lock/unlock pairs in Annot to correct place
Rebased patch

Description Thomas Freitag 2013-01-27 11:18:03 UTC
Created attachment 73724 [details] [review]
thread-safe qt4 frontend

I found now the time to walk over the qt frontend and made the missing methods thread-safe.
I inserted again the POPPLER_QT4_THREADSAFE directive, I needed it to compile okular correctly with either library, but You can remove it if You don't want to have it.
Comment 1 Thomas Freitag 2013-01-27 11:26:42 UTC
Created attachment 73725 [details] [review]
okular testcase

And here my okular testcase.
@Adam: I allow me to add You to the CC list. One of the reasons is that I can't reconstruct the problems with Page::search, okular uses Page::textList instead of it, and that was already thread-safe with commit of bug 50992. If You still get problems with Page::search, can You provide a testcase for it or tell me, how I can reconstruct it?
Comment 2 Adam Reichold 2013-01-27 11:57:17 UTC
(In reply to comment #1)
> Created attachment 73725 [details] [review] [review]
> okular testcase
> 
> And here my okular testcase.
> @Adam: I allow me to add You to the CC list. One of the reasons is that I
> can't reconstruct the problems with Page::search, okular uses Page::textList
> instead of it, and that was already thread-safe with commit of bug 50992. If
> You still get problems with Page::search, can You provide a testcase for it
> or tell me, how I can reconstruct it?

Thanks for keeping me in the loop! As you already pointed out on the mailing list, changing the call to displayPage using the TextOutputDev and hence this patch seems to have solved the search-while-render problem.

Concerning the POPPLER_QT4_THREADSAFE definition, I could personally live without it since I build with defines for the Poppler version given by pkg-config. But I also do not see a problem with it and advertising this feature is probably nice as well.

To round it up and get a definite statement: With this patch applied, I can consider the complete qt4 frontend thread-safe? Meaning that I can remove all locks that protect a document object and its children? (Including life cycle stuff like adding annotations?)
Comment 3 Thomas Freitag 2013-01-27 12:07:48 UTC
(In reply to comment #2)
> To round it up and get a definite statement: With this patch applied, I can
> consider the complete qt4 frontend thread-safe? Meaning that I can remove
> all locks that protect a document object and its children? (Including life
> cycle stuff like adding annotations?)

Yes, as far as I can see it, all should be thread-safe now. If You find something missing before this bug is closed, add it here, otherwise open a new bug.
Comment 4 Adam Reichold 2013-01-27 12:11:59 UTC
(In reply to comment #3)
> (In reply to comment #2)
> > To round it up and get a definite statement: With this patch applied, I can
> > consider the complete qt4 frontend thread-safe? Meaning that I can remove
> > all locks that protect a document object and its children? (Including life
> > cycle stuff like adding annotations?)
> 
> Yes, as far as I can see it, all should be thread-safe now. If You find
> something missing before this bug is closed, add it here, otherwise open a
> new bug.

Thanks for clarifying that. I'll do some more testing then...
Comment 5 Albert Astals Cid 2013-01-27 22:01:18 UTC
Personally i don't think the define makes sense, just depend on the poppler version and that's it.

There's on true that shold be gTrue to be pedantically correct

Why the setoverprint code move? (I think setoverprint should be also gone from globalparams and move to the outputdev itself, this way we can have one with overprint and one without overprint with no fear that it'll collide one with eachother, but that's for the future :D)

It'd be cool it someone could do some silly test with two threads one rendering all the time and the other randomly querying stuff like fonts or adding annotations and run it in a loop to make sure no crash happens (using okular as test case is a bit heavy and difficult)
Comment 6 Adam Reichold 2013-01-28 06:28:20 UTC
(In reply to comment #5)
> It'd be cool it someone could do some silly test with two threads one
> rendering all the time and the other randomly querying stuff like fonts or
> adding annotations and run it in a loop to make sure no crash happens (using
> okular as test case is a bit heavy and difficult)

I'll try to create something suitable today.
Comment 7 Adam Reichold 2013-01-28 07:24:43 UTC
Created attachment 73759 [details]
test case for multi-threaded access

First try for a test case for stressing multi-threaded document access using Poppler's qt4 frontend.
Comment 8 Adam Reichold 2013-01-28 07:28:18 UTC
(In reply to comment #6)
> (In reply to comment #5)
> > It'd be cool it someone could do some silly test with two threads one
> > rendering all the time and the other randomly querying stuff like fonts or
> > adding annotations and run it in a loop to make sure no crash happens (using
> > okular as test case is a bit heavy and difficult)
> 
> I'll try to create something suitable today.

I attached my first attempt at such a test case and currently it crashes in Document::fonts. Actually, sometimes it outright crashes and sometimes there are just tons of error messages, mostly about of unexpected ends of flate streams.

I'll try to enlarge test coverage as we progress.
Comment 9 Thomas Freitag 2013-01-28 15:01:21 UTC
Thanks a lot for Your test case, Adam. Two reasóns for thanking
a) I learned a little bit more about qt programming. It took me some time to figure out how to compile Your test case, but it wasn't wasted time. I encountered how easy it is to setup threads with qt, perhaps this helps me anywhere else sometimes.
b) Your test case shows me really fast that I missed to fix the FontInfo stuff: it uses the page resource dict which is manipulated during rendering of this page and can't be used by FontInfo during the page is rendered. (BTW, same also occurs when creating / removing annotations, I haven't seen that, but rarely it causes crashes, too)

I was already able to solve these problems, Your test case runs now since 30 minutes without any problems. But before I upload the changes I want change the sources a little bit more according to Albert hints, and because I've now some other dates that have to be wait at least until tomorrow.
Comment 10 Adam Reichold 2013-01-28 17:06:09 UTC
Created attachment 73783 [details]
test case for multi-threaded document access

The second attempt to stress test multi-threaded document access in Poppler's qt4 frontend.
Comment 11 Adam Reichold 2013-01-28 17:08:37 UTC
(In reply to comment #9)
> I was already able to solve these problems, Your test case runs now since 30
> minutes without any problems. But before I upload the changes I want change
> the sources a little bit more according to Albert hints, and because I've
> now some other dates that have to be wait at least until tomorrow.

Hello Thomas,

I am glad that you liked it. Since the initial problem is solved, I tried to increase the stress level by expanding the test case. This includes using several documents concurrently, more annotation access and a third "crazy" thread which seems to be able to poke out the annotation issues in the current patch as well.

Best regards, Adam.
Comment 12 Thomas Freitag 2013-01-29 10:45:00 UTC
Created attachment 73820 [details] [review]
fix font info stuff, share annot over threads

(In reply to comment #5)
> Personally i don't think the define makes sense, just depend on the poppler
> version and that's it.

Ok, I removed it from this patch

> There's on true that shold be gTrue to be pedantically correct

Ok, I changed it. But to be pedantically, too, I only correct MY changes, NOT the other uses of true/false :-)

> Why the setoverprint code move? (I think setoverprint should be also gone
> from globalparams and move to the outputdev itself, this way we can have one
> with overprint and one without overprint with no fear that it'll collide one
> with eachother, but that's for the future :D)

Future is there, done.

!!!!!!!!!!!!WARNING!!!!!!!!!!!!
Running Adam's test, I encounterd that there is an "unlock" missing in XRef.cc. This can cause deadlocks with the actual git master, so this part of the patch should be committed asap to avoid bug reports. It will be solved in better way in bug 59933, but I stopped working on it because I think we should close this bug first!

Another comment to this patch: I insert some locks/unlocks in the Annot stuff, because Adam's test case shares annotations over threads and can modify an annotation at the same time in differnt threads. I don't think that this is a normal use case, but nevertheless, better to have it!
Comment 13 Thomas Freitag 2013-01-29 14:57:14 UTC
Created attachment 73834 [details] [review]
move lock/unlock pairs in Annot to correct place

Sorry if wasting anybodys time: I made some additional tests with Adam's test-case and encountered, that there are problems when drawing annots during rendering pages: the draw routines doesn't use only local variables but modifies also instance variables (appearBuf), and therefore definitely need to be locked!

Here the patch which does it correctly!
Comment 14 Albert Astals Cid 2013-02-18 23:04:55 UTC
Can you rebase the patch? Doesn't apply cleanly anymore
Comment 15 Thomas Freitag 2013-02-19 12:40:01 UTC
Created attachment 75109 [details] [review]
Rebased patch

Here a rebased patch
Comment 16 Albert Astals Cid 2013-02-20 22:16:23 UTC
Commited, are we done in this one?

P.S: Just had a look at the construct of Page::getResourceDict and i think it's a bit unseasy to use, do you think that in the "cleanup" bug we could instead use two functions? One that always does the copy and one that not? Now you have to keep track if you passed down a non null XRef to know if you have to delete it or not, which i think may be a bit cumbersome and error prone, what do you think?
Comment 17 Thomas Freitag 2013-02-21 07:56:24 UTC
(In reply to comment #16)
> Commited, are we done in this one?
> 
> P.S: Just had a look at the construct of Page::getResourceDict and i think
> it's a bit unseasy to use, do you think that in the "cleanup" bug we could
> instead use two functions? One that always does the copy and one that not?
> Now you have to keep track if you passed down a non null XRef to know if you
> have to delete it or not, which i think may be a bit cumbersome and error
> prone, what do you think?

I'LL have a look at it abd try to find a better solution in bug 59933.

BTW, during my tests with Adam's program I got one or two times crashes. But the reason for it was the test program itself: two threads fetched the page annots (without any problems) and the deleted the same annotation, the first one wins and the second could crash. I cannot solve that in the library, this is the one case I know where the calling program is responsible to lock two threads against each other.

@Adam: Any known open issues or can we close this bug now?
Comment 18 Adam Reichold 2013-02-21 16:28:11 UTC
(In reply to comment #17)
> (In reply to comment #16)
> > Commited, are we done in this one?
> > 
> > P.S: Just had a look at the construct of Page::getResourceDict and i think
> > it's a bit unseasy to use, do you think that in the "cleanup" bug we could
> > instead use two functions? One that always does the copy and one that not?
> > Now you have to keep track if you passed down a non null XRef to know if you
> > have to delete it or not, which i think may be a bit cumbersome and error
> > prone, what do you think?
> 
> I'LL have a look at it abd try to find a better solution in bug 59933.
> 
> BTW, during my tests with Adam's program I got one or two times crashes. But
> the reason for it was the test program itself: two threads fetched the page
> annots (without any problems) and the deleted the same annotation, the first
> one wins and the second could crash. I cannot solve that in the library,
> this is the one case I know where the calling program is responsible to lock
> two threads against each other.

I agree, as is stated in the documentation, the caller is responsible that no more objects refer to a specific annotation before calling "Page::removeAnnotation", so this is invalid usage, even if it is done in a single thread.

> @Adam: Any known open issues or can we close this bug now?

Nothing comes to mind.
Comment 19 Albert Astals Cid 2013-02-21 16:42:05 UTC
Ok, closed then

Use of freedesktop.org services, including Bugzilla, is subject to our Code of Conduct. How we collect and use information is described in our Privacy Policy.