-
Notifications
You must be signed in to change notification settings - Fork 813
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enable PendingJob use in PJSUA2 swig generated wrapper class #3774
Conversation
This was originally my suggestion, but I think something more is required, because the job processing code frees the pointer out from under the python (or whatever) interface and causes crashes. |
The issue is the registered/added |
The solution only prevents the deletion by the library, but will place the burden of the memory management of the jobs to the app, which may not be as simple and obvious how. |
This PR approach (the
I think the docs better describe the above, and as @sauwming mentioned, an example is even better. There may be an alternative approach, using SWIG |
pjsip/include/pjsua2/endpoint.hpp
Outdated
* If this is set to true, the job will be automatically be deleted | ||
* after the job is executed. | ||
*/ | ||
bool autoDelete; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if this is a private member, initialized via constructor param with the default is true (as existing)?
SWIG has option to set the object ownership, unfortunately the names are different for each language. |
pjsip/include/pjsua2/endpoint.hpp
Outdated
@@ -1162,11 +1162,24 @@ struct EpConfig : public PersistentObject | |||
/* This represents posted job */ | |||
struct PendingJob | |||
{ | |||
PendingJob():autoDelete(true){}; | |||
|
|||
PendingJob(bool autoDel):autoDelete(autoDel) {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Instead of having two constructors, why not just one with default param, e.g:
PendingJob(bool autoDel=true)
? - IMO it is better to have the docs about maintaining reference to object
PendingJob
for Python/Java, manual delete, destructor called from PJLIB thread, and mini sample code (or link to sample) here?
pjsip/include/pjsua2/endpoint.hpp
Outdated
/** Perform the job */ | ||
virtual void execute(bool is_pending) = 0; | ||
|
||
bool getAutoDelete() { return autoDelete; }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO if this method is only useful for Endpoint
, perhaps use class friend
feature is cleaner?
The example seems rather complicated and places quite some responsibilities on the application side, that I think it may be worth redesigning the API and class. Some (rough) ideas:
The objective is to make it as simple as possible for the user to execute a job in the main thread, so feel free to get creative and modify existing APIs and/or classes. If backward compatibility is a concern, we can simply keep the current APIs, and create a new set of APIs and classes instead (such as |
In this case, is there a reason to use C++98/03? template <typename Job>
void Endpoint::utilAddPendingJob(Job &&job)
{
... std::forward<Job>(job)...
}
...
ep.utilAddPendingJob([some_string](){cout << "Hello world" << some_string;}); (in C++ you could even use void Endpoint::utilAddPendingJob(std::function<....> job)
{
...
} |
The cool thing about C++ is that it supports overload sets. So there is no reason to use a new unction name, as long as the signature is different. |
IMHO duplicating the job may not be feasible, we don't really know about the job object so we don't know how to duplicate it, even if we could, it can be complicated, for example, if 'the job' has some reference counter to manage, the duplicate won't work with the ref counter? |
I believe we can define a virtual clone function ( But to avoid misunderstanding, it doesn't have to be this way, i.e. it doesn't have to involve duplication and such. The objective is to enable the user to execute a job in the main thread, in the simplest way possible. Surely there must be a solution for this? The post by @wosrediinanatour suggest some alternatives that can be explored. |
Just another idea: functionality looks similar to the ASIO's post function template: https://think-async.com/Asio/asio-1.18.0/doc/asio/reference/post.html - a very popular library, which is well designed. |
I tried to implement the suggestion by @sauwming, by copying the input
//Endpoint.hpp
struct PendingJob {
...
virtual PendingJob *clone() const = 0;
...
};
class Endpoint {
...
void utilAddPendingJob2(const PendingJob *job);
...
}; //Endpoint.cpp
void Endpoint::utilAddPendingJob2(const PendingJob *job)
{
PendingJob *newJob = job->clone();
utilAddPendingJob(newJob);
}
//Existing PendingJob derived class needs to implement clone() #test.py
class MyJob(pj.PendingJob):
def clone(self):
#use copy instead copy constructor
return copy.copy(self) //sample.java
class MyJob extends PendingJob
{
@Override
public PendingJob clone() {
MyJob job = new MyJob(this);
return job;
}
public MyJob(MyJob job) {}
} |
Looks good! Does it work as expected? If yes, IMO we can use this approach since it looks simple enough. |
Yes, the test app doesn't crash and the |
Try to check if the |
Further testing showed that the class TestJob(pj.PendingJob):
...
def clone(self):
newJob = TestJob(self)
return newJob.__disown__()
... On java: class MyJob extends PendingJob
{
@Override
public PendingJob clone() {
MyJob newJob = new MyJob(this);
return newJob;
}
public MyJob(MyJob job) {
...
this.swigCMemOwn = false;
}
} We are back to the original issue, e.g: caller needs to know specific API from SWIG for this to work. |
Another idea, maintain pending jobs references in a list in each target language, so pending jobs won't be destroyed by GC. Perhaps need to make PendingJob wrapper class, so after the |
Just another note/question?. Why do you use a function If you also need to shallow copy, then just define a move-assignment operator, i.e. |
@wosrediinanatour, the issue is actually not with C++. Current PendingJob mechanism works fine with C++, but we need a solution that can make it work for other languages such as Java, Python, C#. |
I tried using reference counting alternative as described in https://www.swig.org/Doc4.1/SWIGPlus.html#SWIGPlus_ref_unref. |
Nice finding. |
There are 2 alternatives:
|
So is this PR done or still in progress? |
The reference count works as expected. |
From my own testing, it doesn't seem that the reference counting helps prevent the premature deletion. In summary, it still fails both test 2 and test 3 below, the object Job will still be deleted regardless of the reference counting, and it will still crash. This is the test code:
And here's the result:
|
Continued in #3817. |
In order for PendingJob to be utilized, it must function as a base class. This patch will enable the use of PendingJob in SWIG generated wrapper class (e.g: java, cs, python).