1

I am using a library (oatpp web service framework) that launches threads that call my handlers. So I have no control over the outermost code of the threads to insert __try...__except there (I need it for core dumps). The handler implementations look like the following:

shared_ptr<oatpp::web::server::HttpRequestHandler::OutgoingResponse> DownloadRequestHandler::handle(
  const std::shared_ptr<IncomingRequest>& request)
{
  __try {
    return handleGuarded(request);
  }
  __except (Debugging::GenerateDump(GetExceptionInformation(), GetExceptionCode())) {
    quick_exit(4);
  }
}

So the point is that this method needs to forward the response object returned from the guarded method and this object (shared_ptr) is unwindable. Because of this, I'm getting error C2712: Cannot use __try in functions that require object unwinding.

Could you please advise on how to work around this problem?

Serge Rogatch
  • 13,865
  • 7
  • 86
  • 158
  • How about actually fixing whatever bug is making you use non-standard `__try`/`__except` in order to handle segfaults? Using them does not magically fix memory corruption and other undefined behavior that generates the hardware exception. At this point your C++ program is completely and irrevocably broken, and attempting to limp along after undefined behavior occurs will only end in more tears. Using `__try`/`__except` is never a fix for the "my program crashes" problem. It is a non-standard Microsoft exception/extension only for use in low-level drivers. – Sam Varshavchik Jun 05 '20 at 10:52
  • [Compiler Error C2712](https://learn.microsoft.com/en-us/cpp/error-messages/compiler-errors-2/compiler-error-c2712?view=vs-2019) advice 3 Possible solutions. hard say more – RbMm Jun 05 '20 at 10:55
  • Does this answer your question? [very simple code, and getting error C2712, could not understand why](https://stackoverflow.com/questions/24748384/very-simple-code-and-getting-error-c2712-could-not-understand-why) – Richard Critten Jun 05 '20 at 10:58
  • @RichardCritten, no, they don't because in those questions the OPs had control over low-level thread entry points. – Serge Rogatch Jun 05 '20 at 10:59
  • @RbMm, thanks, but those solutions just say what to do without saying how. It is not straightforward, how to accomplish the recommendations. – Serge Rogatch Jun 05 '20 at 11:00
  • @SamVarshavchik, unless you are a god of programming with a sub-quadratic solution for Boolean Satisfiability problem, you can't claim that your non-trivial program is completely correct :) . `__try`/`__except` is a last resort option I want to have for making the program even more stable in the next iteration after crash dump analysis. – Serge Rogatch Jun 05 '20 at 11:12
  • But, for example, by using iterators and algorithms correctly I can prove that it is logically impossible for my program to run off past the end of an array and corrupt memory. This is like saying that to drive safely and follow all driving regulations, all I have to do is make sure that my airbag is in working order. – Sam Varshavchik Jun 05 '20 at 11:31
  • @SamVarshavchik, you still need the "airbag" because a human is incapable of driving totally safely because a human mind cannot take all the circumstances into account. But driving is much easier than programming. In programming, to test that a (non-trivial) program has no bugs you need a sub-quadratic P=NP solution. – Serge Rogatch Jun 05 '20 at 11:35
  • It seems that if I were to adopt this kind of thinking I must reach the opposite conclusion: programming is easier than driving, because I can logically prove that a given algorithm does not result in undefined behavior, when I can't prove the same of other drivers on the road. I'm not sure why someone needs a "sub-quadratic P=NP solution" for that. Somehow I manage to do it without needing one. – Sam Varshavchik Jun 05 '20 at 11:56
  • @SamVarshavchik, unfortunately, theorem proving task is dependent on the solution for Boolean Satisfiability problem, meaning there is no polynomial algorithm for proving anything including the correctness of programs. You can prove some toy theorems or trivial programs using brute-force or educated guessing, but this doesn't work for large programs/problems. Besides this theoretical aspect, there is also the human factor: do you never make a typo in the program? I don't mean typos that a compiler can correct. – Serge Rogatch Jun 05 '20 at 12:06
  • It is not necessary to use the "Boolean Satisfiability problem", or involve any "polynomial algorithms", or a "sub-quadratic P=NP solution", or any kind of buzzword bingo to prove the correctness of basic algorithms and "toy theorems". Once they're proven, it is similarly trivial to prove that, when combined together, the product is still algorithmically correct. Professional developers don't write a huge pile of C++ code and only then try to see if it's correct. As far as typos go, one learns programming practices where typos result in easily-caught compilation errors, and thus identified. – Sam Varshavchik Jun 05 '20 at 12:17
  • 1
    Why don't you set up an unhandled exception filter instead? – IInspectable Jun 05 '20 at 13:17
  • @IInspectable, indeed, you can post that as the solution. – Serge Rogatch Jun 05 '20 at 16:46

3 Answers3

1

I would propose two options:

1) Extend or Implement your own HttpConnectionHandler and spawn threads the way you want. For this, you need to override the handleConnection method.

Something like this:

void handleConnection(const std::shared_ptr<oatpp::data::stream::IOStream>& connection,
                      const std::shared_ptr<const ParameterMap>& params) override {

  ...

  std::thread t([m_components, connection]{
    HttpProcessor::Task task(m_components, connection)
    __try {
      task.run(); // run guarded
    __except (Debugging::GenerateDump(GetExceptionInformation(), GetExceptionCode())) {
      quick_exit(4);
    }
  })

  ...

}

The rest of the code can be just copy-pasted. Currently, the m_components var is a private member of HttpConnectionHandler, which prevents you to inherit directly from HttpConnectionHandler, but you can make a PR to make it protected - thus you'll need to override one method only.

  • The advantage of this approach - is, that, handling exceptions at this level enables you to use oatpp ApiController with all its request mappings.

  • The disadvantage - you won't be able to return a response to the client in case of core-dumps.

2) The second option would be to create a PR to oatpp to add a compiler option for adding an additional __try __except block in HttpProcessor

Thus the client could be informed about core-dumps.

lganzzzo
  • 558
  • 3
  • 10
1

Instead of observing SEH exceptions in situ it's more conventional to set up an unhandled exception filter. The system provides the SetUnhandledExceptionFilter API to register a callback for a thread in case an SEH exception goes unhandled (C++ exceptions are built on top of SEH exceptions, too, so this callback also gets invoked for unhandled C++ exceptions).

To properly instrument every thread in the target process with an exception filter, it's required to get notified of any thread that gets created. A DLL's entry point gets called whenever a thread is created with an fdwReason code of DLL_THREAD_ATTACH. Calling SetUnhandledExceptionFilter from there is safe.

The UnhandledExceptionFilter callback gets a pointer to an EXCEPTION_POINTERS struct, which is required for a meaningful minidump (including the location in code, where the exception was raised). Make sure to do the absolute minimum you can possibly get away with in response to an unhandled exception, i.e. transfer the EXCEPTION_POINTERS to an external process, have it write out the dump, and terminate your process when the dump was written.

IInspectable
  • 46,945
  • 8
  • 85
  • 181
0

So far I have come up with the following solution that involves 3 intermediate functions. More concise solutions are welcome!

shared_ptr<oatpp::web::server::HttpRequestHandler::OutgoingResponse> DownloadRequestHandler::handle(
  const std::shared_ptr<IncomingRequest>& request)
{
  shared_ptr<oatpp::web::server::HttpRequestHandler::OutgoingResponse> response;
  handleGuarded1(request, response);
  return response;
}

void DownloadRequestHandler::handleGuarded1(const std::shared_ptr<IncomingRequest>& request,
  std::shared_ptr<OutgoingResponse>& response)
{
  __try {
    handleGuarded2(request, response);
  }
  __except (Debugging::GenerateDump(GetExceptionInformation(), GetExceptionCode())) {
    quick_exit(4);
  }
}

void DownloadRequestHandler::handleGuarded2(const std::shared_ptr<IncomingRequest>& request,
  std::shared_ptr<OutgoingResponse>& response)
{
  response = handleGuarded(request);
}

shared_ptr<oatpp::web::server::HttpRequestHandler::OutgoingResponse> DownloadRequestHandler::handleGuarded(
  const std::shared_ptr<IncomingRequest>& request)
{
    // Do the actual work...
}
Serge Rogatch
  • 13,865
  • 7
  • 86
  • 158