Skip to content
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

[C++] Wait until event loop terminates when closing the Client #15316

Merged

Conversation

BewareMyPower
Copy link
Contributor

@BewareMyPower BewareMyPower commented Apr 25, 2022

Fixes #13267

Motivation

Unlike Java client, the Client of C++ client has a shutdown method
that is responsible to execute the following steps:

  1. Call shutdown on all internal producers and consumers
  2. Close all connections in the pool
  3. Close all executors of the executor providers.

When an executor is closed, it call io_service::stop(), which makes
the event loop (io_service::run()) in another thread return as soon as
possible. However, there is no wait operation. If a client failed to
create a producer or consumer, the close method will call shutdown
and close all executors immediately and exits the application. In this
case, the detached event loop thread might not exit ASAP, then valgrind
will detect the memory leak.

This memory leak can be avoided by sleeping for a while after
Client::close returns or there are still other things to do after
that. However, we should still adopt the semantics that after
Client::shutdown returns, all event loop threads should be terminated.

Modifications

  • Add a timeout parameter to the close method of ExecutorService and
    ExecutorServiceProvider as the max blocking timeout if it's
    non-negative.
  • Add a TimeoutProcessor helper class to update the left timeout after
    calling all methods that accept the timeout parameter.
  • Call close on all ExecutorServiceProviders in
    ClientImpl::shutdown with 500ms timeout, which could be long enough.
    In addition, in handleClose method, call shutdown in another
    thread to avoid the deadlock.

Verifying this change

After applying this patch, the reproduce code in #13267 will pass the
valgrind check.

==3013== LEAK SUMMARY:
==3013==    definitely lost: 0 bytes in 0 blocks
==3013==    indirectly lost: 0 bytes in 0 blocks
==3013==      possibly lost: 0 bytes in 0 blocks

Documentation

Check the box below or label this PR directly.

Need to update docs?

  • doc-required
    (Your PR needs to update docs and you will update later)

  • no-need-doc
    (Please explain why)

  • doc
    (Your PR contains doc changes)

  • doc-added
    (Docs have been already added)

Fixes apache#13267

### Motivation

Unlike Java client, the `Client` of C++ client has a `shutdown` method
that is responsible to execute the following steps:
1. Call `shutdown` on all internal producers and consumers
2. Close all connections in the pool
3. Close all executors of the executor providers.

When an executor is closed, it call `io_service::stop()`, which makes
the event loop (`io_service::run()`) in another thread return as soon as
possible. However, there is no wait operation. If a client failed to
create a producer or consumer, the `close` method will call `shutdown`
and close all executors immediately and exits the application. In this
case, the detached event loop thread might not exit ASAP, then valgrind
will detect the memory leak.

This memory leak can be avoided by sleeping for a while after
`Client::close` returns or there are still other things to do after
that. However, we should still adopt the semantics that after
`Client::shutdown` returns, all event loop threads should be terminated.

### Modifications
- Add a timeout parameter to the `close` method of `ExecutorService` and
  `ExecutorServiceProvider` as the max blocking timeout if it's
  non-negative.
- Add a `TimeoutProcessor` helper class to update the left timeout after
  calling all methods that accept the timeout parameter.
- Call `close` on all `ExecutorServiceProvider`s in
  `ClientImpl::shutdown` with 500ms timeout, which could be long enough.
  In addition, in `handleClose` method, call `shutdown` in another
  thread to avoid the deadlock.

### Verifying this change

After applying this patch, the reproduce code in apache#13627 will pass the
valgrind check.

```
==3013== LEAK SUMMARY:
==3013==    definitely lost: 0 bytes in 0 blocks
==3013==    indirectly lost: 0 bytes in 0 blocks
==3013==      possibly lost: 0 bytes in 0 blocks
```
@BewareMyPower BewareMyPower added this to the 2.11.0 milestone Apr 25, 2022
@BewareMyPower BewareMyPower self-assigned this Apr 25, 2022
@BewareMyPower BewareMyPower changed the title [C++] Wait until event loops terminates when closing the Client [C++] Wait until event loop terminates when closing the Client Apr 25, 2022
@github-actions github-actions bot added the doc-not-needed Your PR changes do not impact docs label Apr 25, 2022
@BewareMyPower BewareMyPower marked this pull request as draft April 25, 2022 16:25
@BewareMyPower BewareMyPower marked this pull request as ready for review April 25, 2022 16:54
@BewareMyPower BewareMyPower merged commit cd78f39 into apache:master Apr 27, 2022
@BewareMyPower BewareMyPower deleted the bewaremypower/cpp-executor-wait branch April 27, 2022 07:10
codelipenghui pushed a commit that referenced this pull request Apr 28, 2022
* [C++] Wait until event loops terminates when closing the Client

Fixes #13267

### Motivation

Unlike Java client, the `Client` of C++ client has a `shutdown` method
that is responsible to execute the following steps:
1. Call `shutdown` on all internal producers and consumers
2. Close all connections in the pool
3. Close all executors of the executor providers.

When an executor is closed, it call `io_service::stop()`, which makes
the event loop (`io_service::run()`) in another thread return as soon as
possible. However, there is no wait operation. If a client failed to
create a producer or consumer, the `close` method will call `shutdown`
and close all executors immediately and exits the application. In this
case, the detached event loop thread might not exit ASAP, then valgrind
will detect the memory leak.

This memory leak can be avoided by sleeping for a while after
`Client::close` returns or there are still other things to do after
that. However, we should still adopt the semantics that after
`Client::shutdown` returns, all event loop threads should be terminated.

### Modifications
- Add a timeout parameter to the `close` method of `ExecutorService` and
  `ExecutorServiceProvider` as the max blocking timeout if it's
  non-negative.
- Add a `TimeoutProcessor` helper class to update the left timeout after
  calling all methods that accept the timeout parameter.
- Call `close` on all `ExecutorServiceProvider`s in
  `ClientImpl::shutdown` with 500ms timeout, which could be long enough.
  In addition, in `handleClose` method, call `shutdown` in another
  thread to avoid the deadlock.

### Verifying this change

After applying this patch, the reproduce code in #13627 will pass the
valgrind check.

```
==3013== LEAK SUMMARY:
==3013==    definitely lost: 0 bytes in 0 blocks
==3013==    indirectly lost: 0 bytes in 0 blocks
==3013==      possibly lost: 0 bytes in 0 blocks
```

(cherry picked from commit cd78f39)
codelipenghui pushed a commit that referenced this pull request Apr 28, 2022
* [C++] Wait until event loops terminates when closing the Client

Fixes #13267

### Motivation

Unlike Java client, the `Client` of C++ client has a `shutdown` method
that is responsible to execute the following steps:
1. Call `shutdown` on all internal producers and consumers
2. Close all connections in the pool
3. Close all executors of the executor providers.

When an executor is closed, it call `io_service::stop()`, which makes
the event loop (`io_service::run()`) in another thread return as soon as
possible. However, there is no wait operation. If a client failed to
create a producer or consumer, the `close` method will call `shutdown`
and close all executors immediately and exits the application. In this
case, the detached event loop thread might not exit ASAP, then valgrind
will detect the memory leak.

This memory leak can be avoided by sleeping for a while after
`Client::close` returns or there are still other things to do after
that. However, we should still adopt the semantics that after
`Client::shutdown` returns, all event loop threads should be terminated.

### Modifications
- Add a timeout parameter to the `close` method of `ExecutorService` and
  `ExecutorServiceProvider` as the max blocking timeout if it's
  non-negative.
- Add a `TimeoutProcessor` helper class to update the left timeout after
  calling all methods that accept the timeout parameter.
- Call `close` on all `ExecutorServiceProvider`s in
  `ClientImpl::shutdown` with 500ms timeout, which could be long enough.
  In addition, in `handleClose` method, call `shutdown` in another
  thread to avoid the deadlock.

### Verifying this change

After applying this patch, the reproduce code in #13627 will pass the
valgrind check.

```
==3013== LEAK SUMMARY:
==3013==    definitely lost: 0 bytes in 0 blocks
==3013==    indirectly lost: 0 bytes in 0 blocks
==3013==      possibly lost: 0 bytes in 0 blocks
```

(cherry picked from commit cd78f39)
@codelipenghui codelipenghui added the cherry-picked/branch-2.8 Archived: 2.8 is end of life label Apr 28, 2022
codelipenghui pushed a commit that referenced this pull request Apr 29, 2022
* [C++] Wait until event loops terminates when closing the Client

Fixes #13267

### Motivation

Unlike Java client, the `Client` of C++ client has a `shutdown` method
that is responsible to execute the following steps:
1. Call `shutdown` on all internal producers and consumers
2. Close all connections in the pool
3. Close all executors of the executor providers.

When an executor is closed, it call `io_service::stop()`, which makes
the event loop (`io_service::run()`) in another thread return as soon as
possible. However, there is no wait operation. If a client failed to
create a producer or consumer, the `close` method will call `shutdown`
and close all executors immediately and exits the application. In this
case, the detached event loop thread might not exit ASAP, then valgrind
will detect the memory leak.

This memory leak can be avoided by sleeping for a while after
`Client::close` returns or there are still other things to do after
that. However, we should still adopt the semantics that after
`Client::shutdown` returns, all event loop threads should be terminated.

### Modifications
- Add a timeout parameter to the `close` method of `ExecutorService` and
  `ExecutorServiceProvider` as the max blocking timeout if it's
  non-negative.
- Add a `TimeoutProcessor` helper class to update the left timeout after
  calling all methods that accept the timeout parameter.
- Call `close` on all `ExecutorServiceProvider`s in
  `ClientImpl::shutdown` with 500ms timeout, which could be long enough.
  In addition, in `handleClose` method, call `shutdown` in another
  thread to avoid the deadlock.

### Verifying this change

After applying this patch, the reproduce code in #13627 will pass the
valgrind check.

```
==3013== LEAK SUMMARY:
==3013==    definitely lost: 0 bytes in 0 blocks
==3013==    indirectly lost: 0 bytes in 0 blocks
==3013==      possibly lost: 0 bytes in 0 blocks
```

(cherry picked from commit cd78f39)
@codelipenghui codelipenghui added the cherry-picked/branch-2.9 Archived: 2.9 is end of life label Apr 29, 2022
nicoloboschi pushed a commit to datastax/pulsar that referenced this pull request May 4, 2022
…e#15316)

* [C++] Wait until event loops terminates when closing the Client

Fixes apache#13267

### Motivation

Unlike Java client, the `Client` of C++ client has a `shutdown` method
that is responsible to execute the following steps:
1. Call `shutdown` on all internal producers and consumers
2. Close all connections in the pool
3. Close all executors of the executor providers.

When an executor is closed, it call `io_service::stop()`, which makes
the event loop (`io_service::run()`) in another thread return as soon as
possible. However, there is no wait operation. If a client failed to
create a producer or consumer, the `close` method will call `shutdown`
and close all executors immediately and exits the application. In this
case, the detached event loop thread might not exit ASAP, then valgrind
will detect the memory leak.

This memory leak can be avoided by sleeping for a while after
`Client::close` returns or there are still other things to do after
that. However, we should still adopt the semantics that after
`Client::shutdown` returns, all event loop threads should be terminated.

### Modifications
- Add a timeout parameter to the `close` method of `ExecutorService` and
  `ExecutorServiceProvider` as the max blocking timeout if it's
  non-negative.
- Add a `TimeoutProcessor` helper class to update the left timeout after
  calling all methods that accept the timeout parameter.
- Call `close` on all `ExecutorServiceProvider`s in
  `ClientImpl::shutdown` with 500ms timeout, which could be long enough.
  In addition, in `handleClose` method, call `shutdown` in another
  thread to avoid the deadlock.

### Verifying this change

After applying this patch, the reproduce code in apache#13627 will pass the
valgrind check.

```
==3013== LEAK SUMMARY:
==3013==    definitely lost: 0 bytes in 0 blocks
==3013==    indirectly lost: 0 bytes in 0 blocks
==3013==      possibly lost: 0 bytes in 0 blocks
```

(cherry picked from commit cd78f39)
(cherry picked from commit c0c67db)
nicoloboschi pushed a commit to datastax/pulsar that referenced this pull request May 9, 2022
…e#15316)

* [C++] Wait until event loops terminates when closing the Client

Fixes apache#13267

### Motivation

Unlike Java client, the `Client` of C++ client has a `shutdown` method
that is responsible to execute the following steps:
1. Call `shutdown` on all internal producers and consumers
2. Close all connections in the pool
3. Close all executors of the executor providers.

When an executor is closed, it call `io_service::stop()`, which makes
the event loop (`io_service::run()`) in another thread return as soon as
possible. However, there is no wait operation. If a client failed to
create a producer or consumer, the `close` method will call `shutdown`
and close all executors immediately and exits the application. In this
case, the detached event loop thread might not exit ASAP, then valgrind
will detect the memory leak.

This memory leak can be avoided by sleeping for a while after
`Client::close` returns or there are still other things to do after
that. However, we should still adopt the semantics that after
`Client::shutdown` returns, all event loop threads should be terminated.

### Modifications
- Add a timeout parameter to the `close` method of `ExecutorService` and
  `ExecutorServiceProvider` as the max blocking timeout if it's
  non-negative.
- Add a `TimeoutProcessor` helper class to update the left timeout after
  calling all methods that accept the timeout parameter.
- Call `close` on all `ExecutorServiceProvider`s in
  `ClientImpl::shutdown` with 500ms timeout, which could be long enough.
  In addition, in `handleClose` method, call `shutdown` in another
  thread to avoid the deadlock.

### Verifying this change

After applying this patch, the reproduce code in apache#13627 will pass the
valgrind check.

```
==3013== LEAK SUMMARY:
==3013==    definitely lost: 0 bytes in 0 blocks
==3013==    indirectly lost: 0 bytes in 0 blocks
==3013==      possibly lost: 0 bytes in 0 blocks
```

(cherry picked from commit cd78f39)
(cherry picked from commit 6d365c9)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Pulsar CPP client mem leak
3 participants