Documentation for API Breaking Changes (#2927)

* Documentation for API Breaking Changes
* Add version 2 of the API, plus update documentation
* Add a static assert to ensure version 1 of API we shipped does not change in size.
This commit is contained in:
Ryan Hill 2020-01-29 15:55:29 -08:00 committed by GitHub
parent 1239de3efe
commit f60badc1f3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 59 additions and 8 deletions

View file

@ -6,8 +6,8 @@
#include <stdint.h>
#include <string.h>
// This value is used in structures passed to ORT so that a newer version of ORT will still work with
#define ORT_API_VERSION 1
// This value is used in structures passed to ORT so that a newer version of ORT will still work with them
#define ORT_API_VERSION 2
#ifdef __cplusplus
extern "C" {
@ -214,6 +214,8 @@ typedef struct OrtApi OrtApi;
struct OrtApiBase {
const OrtApi*(ORT_API_CALL* GetApi)(uint32_t version)NO_EXCEPTION; // Pass in ORT_API_VERSION
// nullptr will be returned if the version is unsupported, for example when using a runtime older than this header file
const char*(ORT_API_CALL* GetVersionString)() NO_EXCEPTION;
};
typedef struct OrtApiBase OrtApiBase;

View file

@ -72,7 +72,7 @@ namespace onnxruntime {
struct CustomOpKernel : OpKernel {
CustomOpKernel(const OpKernelInfo& info, OrtCustomOp& op) : OpKernel(info), op_(op) {
if (op_.version != 1)
if (op_.version > ORT_API_VERSION)
throw std::invalid_argument("Unsupported version '" + std::to_string(op_.version) + "' in custom op '" + op.GetName(&op));
op_kernel_ = op_.CreateKernel(&op_, OrtGetApiBase()->GetApi(op_.version), reinterpret_cast<OrtKernelInfo*>(const_cast<OpKernelInfo*>(&info)));
}

View file

@ -1334,7 +1334,49 @@ static constexpr OrtApiBase ort_api_base = {
&OrtApis::GetVersionString,
};
static constexpr OrtApi ort_api_1 = {
/* Rules on how to add a new Ort API version
In general, NEVER remove or rearrange the members in this structure unless a new version is being created. The
goal is for newer shared libraries of the Onnx Runtime to work with binaries targeting the previous versions.
In order to do that we need to ensure older binaries get the older interfaces they are expecting.
If the next version of the OrtApi only adds members, new members can be added at the end of the OrtApi structure
without breaking anything. In this case, rename the ort_api_# structure in a way that shows the range of versions
it supports, for example 'ort_api_1_to_2', and then GetApi can return the same structure for a range of versions.
If methods need to be removed or rearranged, then make a copy of the OrtApi structure and name it 'OrtApi#to#'.
The latest Api should always be named just OrtApi. Then make a copy of the latest ort_api_* structure below and
name it ort_api_# to match the latest version number supported, you'll need to be sure the structure types match
the API they're for (the compiler should complain if this isn't correct).
If there is no desire to have the headers still expose the older APIs (clutter, documentation, etc) then the
definition should be moved to a file included by this file so that it's still defined here for binary compatibility
but isn't visible in public headers.
So for example, if we wanted to just add some new members to the ort_api_1_to_2, we'd take the following steps:
In include\onnxruntime\core\session\onnxruntime_c_api.h we'd just add the members to the end of the structure
In this file, we'd correspondingly add the member values to the end of the ort_api_1_to_2 structure, and also rename
it to ort_api_1_to_3.
Then in GetApi we'd make it return ort_api_1_to_3 for versions 1 through 3.
Second example, if we wanted to add and remove some members, we'd do this:
In include\onnxruntime\core\session\onnxruntime_c_api.h we'd make a copy of the OrtApi structure and name the
old one OrtApi1to2. In the new OrtApi we'd add or remove any members that we desire.
In this file, we'd create a new copy of ort_api_1_to_2 called ort_api_3 and make the corresponding changes that were
made to the new OrtApi.
In GetApi we now make it return ort_api_3 for version 3.
*/
static constexpr OrtApi ort_api_1_to_2 = {
// NOTE: The ordering of these fields MUST not change after that version has shipped since existing binaries depend on this ordering.
// Shipped as version 1 - DO NOT MODIFY (see above text for more information)
&OrtApis::CreateStatus,
&OrtApis::GetErrorCode,
&OrtApis::GetErrorMessage,
@ -1449,13 +1491,20 @@ static constexpr OrtApi ort_api_1 = {
&OrtApis::ReleaseTensorTypeAndShapeInfo,
&OrtApis::ReleaseSessionOptions,
&OrtApis::ReleaseCustomOpDomain,
// End of Version 1 - DO NOT MODIFY ABOVE (see above text for more information)
// Version 2 - In development, feel free to add/remove/rearrange here
};
ORT_API(const OrtApi*, OrtApis::GetApi, uint32_t version) {
if (version > 1)
return nullptr;
// Assert to do a limited check to ensure Version 1 of OrtApi never changes (will detect an addition or deletion but not if they cancel out each other)
// If this assert hits, read the above 'Rules on how to add a new Ort API version'
static_assert(offsetof(OrtApi, ReleaseCustomOpDomain) / sizeof(void*) == 101, "Size of version 1 API cannot change");
return &ort_api_1;
ORT_API(const OrtApi*, OrtApis::GetApi, uint32_t version) {
if (version >= 1 && version <= 2)
return &ort_api_1_to_2;
return nullptr; // Unsupported version
}
ORT_API(const char*, OrtApis::GetVersionString) {