keep your arms and legs inside the platform

28
Keep Your Hands and Feet Inside the Platform James Borden Software Engineer, Couchbase

Upload: jim-borden

Post on 13-Apr-2017

125 views

Category:

Software


0 download

TRANSCRIPT

Page 1: Keep Your Arms and Legs Inside the Platform

Keep Your Hands and Feet Inside the Platform

James BordenSoftware Engineer, Couchbase

Page 2: Keep Your Arms and Legs Inside the Platform

I AM JAMES BORDENI am on the Internet as @borrrden. I develop Couchbase Lite .NET

Page 3: Keep Your Arms and Legs Inside the Platform

WHAT IS ON THE AGENDA◇What is P/Invoke?◇Example I am working on◇Best Practices I have learned

Page 4: Keep Your Arms and Legs Inside the Platform

WHAT IS P/INVOKE?Let’s start with that…

Page 5: Keep Your Arms and Legs Inside the Platform

Platform Invocation Services, commonly referred to as P/Invoke, is a feature of Common Language Infrastructure implementations, like Microsoft's Common Language Runtime, that enables managed code to call native code.

Page 6: Keep Your Arms and Legs Inside the Platform

WELL WHY DO THAT?◇You have a bazillion lines of legacy code

◇You want to share code between languages (as opposed to architectures)

◇You love pointers (just kidding)

Page 7: Keep Your Arms and Legs Inside the Platform

ELABORATE ON POINT 2◇At Couchbase, we have three versions of our Lite product (Objective-C, Java, .NET)

◇Historically we have developed features for all of them side-by-side

◇That’s a pretty big waste of time

Page 8: Keep Your Arms and Legs Inside the Platform

WELCOME TO THE JUNGLE…I MEAN FORESTEnter into ForestDB and CBForest

Page 9: Keep Your Arms and Legs Inside the Platform

FOREST WHAT?◇ForestDB: Couchbase’s own Key-Value storage engine written in C

◇CBForest: A C++ layer adding Couchbase Lite specific functionality to ForestDB (document creation, view indexing & querying, etc)

Page 10: Keep Your Arms and Legs Inside the Platform

THIS PRESENTS A PROBLEM

We now have unmanaged code being developed by a separate team that need to be utilized by our product. Initially it was easily done with Objective-C++, but what about Java and .NET?

Page 11: Keep Your Arms and Legs Inside the Platform

HOW CAN I DO IT?Let’s start our journey

Page 12: Keep Your Arms and Legs Inside the Platform

NO C++ ALLOWED

*C++ has no standard ABI (Application Binary Interface) and so every function signature changes based on which compiler is used to compile it. This makes C++ an insane and counterproductive idea for P/Invoke. Can you read this small text in the back? No? Good :-p

Page 13: Keep Your Arms and Legs Inside the Platform

HERE IS AN API CALL IN C/** Opaque handle to an opened database. */typedef struct c4Database C4Database;

enum C4DatabaseFlags : uint32_t { kC4DB_Create = 1, /**< Create the file if it doesn't exist */ kC4DB_ReadOnly = 2, /**< Open file read-only */ kC4DB_AutoCompact = 4 /**< Enable auto-compaction */}

typedef struct { const void *buf; size_t size;} C4Slice;

typedef struct { int32_t domain; int32_t code;} C4Error;

C4Database* c4db_open(C4Slice path, C4DatabaseFlags flags, C4Error *outError);

Page 14: Keep Your Arms and Legs Inside the Platform

HERE IS ONE WAY TO DO IT

IntPtr c4db_open(IntPtr path, int flags, out IntPtr outError);

Page 15: Keep Your Arms and Legs Inside the Platform

DON’T DO THAT

*You will spend your entire life with bugs related to putting the wrong type into your method. FOR SHAME!

Page 16: Keep Your Arms and Legs Inside the Platform

OK, WHAT SHOULD I DO?◇Mock the structure layout in C#

◇Use ‘unsafe’ code blocks◇The code will look almost identical to its C counterpart

Page 17: Keep Your Arms and Legs Inside the Platform

WHAT WILL HAPPEN?◇Everything must be copied between managed and unmanaged*

◇The only inherently copyable types are primitives and one dimensional arrays of primitives

*This is also true of passing things to functions in C, and is precisely why pointers exist in the first place.

Page 18: Keep Your Arms and Legs Inside the Platform

HERE IS THE PREVIOUS API IN C#/** Opaque handle to an opened database. */public struct C4Database { }

[Flags]public enum C4DatabaseFlags : uint { Create = 1, ReadOnly = 2, AutoCompact = 4}public unsafe struct C4Slice { public UIntPtr size; // Because size_t is not a fixed size public void* buf; // ZOMG, C# pointer!!}public struct C4Error { public int domain; public int code;} C4Error;

public static extern C4Database* c4db_open(C4Slice path, C4DatabaseFlags flags, C4Error *outError);

Page 19: Keep Your Arms and Legs Inside the Platform

SOME NOTES◇All types are made up of purely blittable types (pointers are simply 32 or 64-bit integers)

◇The runtime is smart enough to figure out what you want (compare to Java)

Page 20: Keep Your Arms and Legs Inside the Platform

WHAT ABOUT SENDING THINGS TO C?◇The garbage collector is non-deterministic and can run at any given time

◇When a garbage collection occurs, all pointers to managed objects are potentially invalidated

Page 21: Keep Your Arms and Legs Inside the Platform

PINNING TO THE RESCUEpublic struct C4String : IDisposable { private GCHandle _handle; // Stores the UTF-8 bytes in a pinned location

public C4String(string s) { _handle = new GCHandle(); if(s != null) { var bytes = Encoding.UTF8.GetBytes(s); _handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); } }}

public unsafe void SendBytes(byte[] bytes) { fixed(byte* ptr = bytes) { SendBytesNative(ptr); // ptr guaranteed not to move }}

Page 22: Keep Your Arms and Legs Inside the Platform

WHAT ABOUT SENDING THINGS TO C#?

◇C# APIs expect C# managed objects

◇C knows nothing about C#

Page 23: Keep Your Arms and Legs Inside the Platform

CATCH THINGS AT THE BOUNDARY

public unsafe string GetString(){ byte *nativeObject = GetNativeObject(); int size = GetNativeSize(); return new string((sbyte*)nativeObject, size, Encoding.UTF8);}

Page 24: Keep Your Arms and Legs Inside the Platform

WHERE TO LOOK FOR NATIVE CODE?

◇The DllImport (DLL Import) attribute and extern keyword indicate native functions

◇The runtime searches a standardized search path for a given DLL

Page 25: Keep Your Arms and Legs Inside the Platform

DLL? YOU MEAN WINDOWS ONLY?

◇Mono, and by extension Xamarin, provides DllMapping functionality to allow you to specify an alternate native library in place of a .dll filename

Page 26: Keep Your Arms and Legs Inside the Platform

EXAMPLE

<configuration> <dllmap dll=”msvcrt.dll" target="libc.dylib" os="osx" /></configuration>

// If the names do not match, you need to manually specify the entry point// This function will look for msvcrt.dll on Windows, and libc.dylib// on OS X[DllImport(“msvcrt.dll”, CallingConvention=CallingConvention.Cdecl, EntryPoint=“memcpy”]public static extern int MemoryCopy(void* dest, void* src, UIntPtr count);

Page 27: Keep Your Arms and Legs Inside the Platform

ANY QUESTIONS?

You can find me [email protected]@couchbase.com

Page 28: Keep Your Arms and Legs Inside the Platform

CREDITS

Special thanks to all the people who made and released these awesome resources for free:◇ Presentation template by SlidesCarnival◇ Photographs by Unsplash &

Death to the Stock Photo (license)