diff --git a/Examples/README.md b/Examples/README.md new file mode 100644 index 0000000..4d1f041 --- /dev/null +++ b/Examples/README.md @@ -0,0 +1,180 @@ +# EMS Examples +#### Table of Contents +* [Simple Loop Benchmarks](#Simple\ Loop\ Benchmarks) +* [Work Queues](#Work\ Queues) +* [Key-Value Store](#kv_store.js) +* [Web Server](#web_server.js) +* [Word Counting](#wordCount.js ) + + +## Simple Loop Benchmarks +The original [STREAMS](https://www.cs.virginia.edu/stream/) +benchmark measures the _Speed Not To Exceed_ +bandwidth of a CPU to it's attached RAM by performing large +numbers of simple operations. This version for +EMS.js measures the rate of different atomic operations on +shared integer data and is implemented using two different parallelization schemes: +Bulk Synchronous Parallel (BSP), and Fork-Join. + +Experimental results are from an AWS +`c4.8xlarge (132 ECUs, 36 vCPUs, 2.9 GHz, Intel Xeon E5-2666v3, 60 GiB memory`. + +
+
+ +### streams_bulk_sync_parallel.js +```bash +node streams_bulk_sync_parallel.js 6 # Use 6 processes +``` +BSP parallelism starts all the processes at the program's main entry point +and all statements are executed, barriers are used to synchronize execution +between loops. + +### streams_fork_join.js +```bash +node streams_fork_join.js 4 # Use 4 processes +``` +FJ parallel execution begins with a single master thread which creates +new threads as needed (ie: to execute a loop) which exit when there is +no work left, at which point the master thread joins + + +## Work Queues +The parameters of the experiment are defined in the `initializeSharedData()` function. +An experiment creates several new EMS arrays and a work queue of +randomly generated transactions that update one or more of the arrays. +Parallel processes dequeue the work and perform atomic updates on +the new EMS arrays. Finally, the experiment performs checksums to ensure +all the transactions were performed properly. + +```javascript + arrLen = 1000000; // Maximum number of elements the EMS array can contain + heapSize = 100000; // Amount of persistent memory to reserve for transactions + nTransactions = 1000000; // Number of transactions to perform in the experiment + nTables = 6; // Number of EMS arrays to perform transactions across + maxNops = 5; // Maximum number of EMS arrays to update during a transaction +``` + +### concurrent_Q_and_TM.js +```bash +node concurrent_Q_and_TM.js 4 # 1 process enqueues then processes work, 3 processes perform work +```` +All processes are started, one enqueues transactions while the others dequeue +and perform the work. When all the work has been enqueued, that process also +begins dequeing and performing work. + +### workQ_and_TM.js +```bash +node workQ_and_TM.js 4 # Enqueue all the work, all 4 processes deqeue and perform work +```` +All the transactions are enqueued, then all the processes begin +to dequeue and perform work until no work remains in the queue. + + + + +## kv_store.js +An EMS array is presented as a Key-Value store with EMS memory operations +presented as a REST interface. +An ordinary Node.js Cluster is started to demonstrate parallelism outside of EMS. + +```bash +node kv_store.js 2 # Start the server, logs will appear on this terminal +``` + +```bash +curl localhost:8080/writeXF?foo=Hello # Unconditionally write "Hello" to the key "foo" and mark Full +curl localhost:8080/faa?foo=+World! # Fetch And Add returns original value: "Hello" +curl localhost:8080/readFF?foo # Read Full and leave Full, final value is "Hello World!" +``` + + +## web_server.js +A parallel region is created for each request received by the web server, +handling the single request with multiple processes. + +```bash +curl http://localhost:8080/?foo=data1 # Insert ephemeral data1 with the key "foo" +curl http://localhost:8080/persist?foo=data2 # Previous data was not persistent, this is a new persistent foo +curl http://localhost:8080/existing/persist?foo=data3 # This data is appended to the already persisted data +curl http://localhost:8080/existing/persist/unique?foo=nowhere # A GUID is generated for this operation +curl http://localhost:8080/existing/persist/reset?foo=data5 # Persisted data is appended to, the response is issued, and the data is deleted +curl http://localhost:8080/existing?foo=final # Persisted reset data is appended to +``` + +## wordCount.js +### Word Counting Using Atomic Operations +Map-Reduce is often demonstrated using word counting because each document can +be processed in parallel, and the results of each document's dictionary reduced +into a single dictionary. This EMS implementation also +iterates over documents in parallel, but it maintains a single shared dictionary +across processes, atomically incrementing the count of each word found. +The final word counts are sorted and the most frequently appearing words +are printed with their counts. + +### Forming the "Bag of Words" with Word Counts +```javascript +var file_list = fs.readdirSync(doc_path); +var splitPattern = new RegExp(/[ \n,\.\\/_\-\<\>:\;\!\@\#\$\%\&\*\(\)=\[\]|\"\'\{\}\?\—]/); +// Iterate over all the files in parallel +ems.parForEach(0, file_list.length, function (fileNum) { + var text = fs.readFileSync(doc_path + file_list[fileNum], 'utf8', "r"); + var words = text.replace(/[\n\r]/g, ' ').toLowerCase().split(splitPattern); + // Sequentially iterate over all the words in one document + words.forEach(function (word) { + wordCounts.faa(word, 1); // Atomically increment the count of times this word was seen + }); +}); +``` + +### Sorting the Word Count list by Frequency +Parallel sorting of the word count is implemented as a multi-step process: + +1. The bag of words is processed by all the processess, each process + building an ordered list of the most common words it encounters +2. The partial lists from all the processes are concatenated into a single list +3. The list of the most common words seen is sorted and truncated + +```javascript +var biggest_counts = new Array(local_sort_len).fill({"key": 0, "count": 0}); +ems.parForEach(0, maxNKeys, function (keyN) { + var key = wordCounts.index2key(keyN); + if (key) { + // Perform an insertion sort of the new key into the biggest_counts + // list, deleting the last (smallest) element to preserve length. + var keyCount = wordCounts.read(key); + var idx = local_sort_len - 1; + while (idx >= 0 && biggest_counts[idx].count < keyCount) { idx -= 1; } + var newBiggest = {"key": key, "count": keyCount}; + if (idx < 0) { + biggest_counts = [newBiggest].concat(biggest_counts.slice(0, biggest_counts.length - 1)); + } else if (idx < local_sort_len) { + var left = biggest_counts.slice(0, idx + 1); + var right = biggest_counts.slice(idx + 1); + biggest_counts = left.concat([newBiggest].concat(right)).slice(0, -1); + } + } +}); +// Concatenate all the partial (one per process) lists into one list +stats.writeXF('most_frequent', []); // Initialize the list +ems.barrier(); // Wait for all procs to have finished initialization +stats.writeEF('most_frequent', stats.readFE('most_frequent').concat(biggest_counts)); +ems.barrier(); // Wait for all procs to have finished merge +ems.master(function() { // Sort & print the list of words, only one process is needed + biggest_counts = stats.readFF('most_frequent'); + biggest_counts.sort(function (a, b) { return b.count - a.count; }); + // Print only the first "local_sort_len" items -- assume the worst case + // of all the largest counts are discovered by a single process + console.log("Most frequently appearing terms:"); + for (var index = 0; index < local_sort_len; index += 1) { + console.log(index + ': ' + biggest_counts[index].key + " " + biggest_counts[index].count); + } +}); +``` + +### Word Count Performance +This section reports the results of running the Word Count example on +documents from Project Gutenberg. +2,981,712,952 words in several languages were parsed, totaling 12,664,852,220 bytes of text. + + diff --git a/Examples/concurrent_Q_and_TM.js b/Examples/concurrent_Q_and_TM.js index 9623b21..2061405 100644 --- a/Examples/concurrent_Q_and_TM.js +++ b/Examples/concurrent_Q_and_TM.js @@ -89,11 +89,11 @@ function timerStop(timer, nOps, label, myID) { // tables to perform transactions on. // function initializeSharedData() { - arrLen = 1000000; - heapSize = 100000; - nTransactions = 1000000; - nTables = 6; - maxNops = 5; + arrLen = 1000000; // Maximum number of elements the EMS array can contain + heapSize = 100000; // Amount of persistent memory to reserve for transactions + nTransactions = 1000000; // Number of transactions to perform in the experiment + nTables = 6; // Number of EMS arrays to perform transactions across + maxNops = 5; // Maximum number of EMS arrays to update during a transaction tables = []; totalNops = ems.new(2); checkNops = ems.new(1); @@ -128,10 +128,9 @@ function initializeSharedData() { // Create 'nTransactions' many transactions, each having a random number (up // to maxNops) randomly chosen values, some of which are read-only. // Because tables and elements are chosen at random, it is necessary to -// remove duplicate elements in a single transaction, +// remove duplicate elements in a single transaction, to prevent deadlocks. // // Transactions are pushed onto a shared queue -// function generateTransactions() { //--------------------------------------------------------------------------- // Generate operations involving random elements in random EMS arrays diff --git a/Examples/kv_store.js b/Examples/kv_store.js index de27445..1f6a016 100644 --- a/Examples/kv_store.js +++ b/Examples/kv_store.js @@ -1,5 +1,5 @@ /*-----------------------------------------------------------------------------+ - | Extended Memory Semantics (EMS) Version 1.2.0 | + | Extended Memory Semantics (EMS) Version 1.3.0 | | Synthetic Semantics http://www.synsem.com/ mogill@synsem.com | +-----------------------------------------------------------------------------+ | Copyright (c) 2011-2014, Synthetic Semantics LLC. All rights reserved. | @@ -30,7 +30,7 @@ | | +-----------------------------------------------------------------------------*/ 'use strict'; -var ems = require('ems')(parseInt(process.argv[2]), true, 'user'); +var ems = require('ems')(1, true, 'user'); var http = require('http'); var url_module = require("url"); var cluster = require('cluster'); @@ -147,8 +147,6 @@ if (cluster.isMaster) { /* Each Slave Cluster processes must connect to the EMS array created * by the master process. */ - delete persistent_data_options.doDataFill; - delete persistent_data_options.dataFill; delete persistent_data_options.setFEtags; persistent_data_options.useExisting = true; persistent_data = ems.new(persistent_data_options); diff --git a/Examples/workQ_and_TM.js b/Examples/workQ_and_TM.js index d90bc68..9be1f21 100644 --- a/Examples/workQ_and_TM.js +++ b/Examples/workQ_and_TM.js @@ -81,11 +81,11 @@ function timerStop(timer, nOps, label, myID) { // tables to perform transactions on. // function initializeSharedData() { - arrLen = 1000000; - heapSize = 100000; - nTransactions = 1000000; - nTables = 6; - maxNops = 5; + arrLen = 1000000; // Maximum number of elements the EMS array can contain + heapSize = 100000; // Amount of persistent memory to reserve for transactions + nTransactions = 1000000; // Number of transactions to perform in the experiment + nTables = 6; // Number of EMS arrays to perform transactions across + maxNops = 5; // Maximum number of EMS arrays to update during a transaction tables = []; totalNops = ems.new(2); checkNops = ems.new(1); @@ -123,10 +123,9 @@ function initializeSharedData() { // Create 'nTransactions' many transactions, each having a random number (up // to maxNops) randomly chosen values, some of which are read-only. // Because tables and elements are chosen at random, it is necessary to -// remove duplicate elements in a single transaction, -// -// Transactions are pushed onto a shared queue +// remove duplicate elements in a single transaction, to prevent deadlocks. // +// Transactions are pushed onto a shared queue function generateTransactions() { //--------------------------------------------------------------------------- // Generate a random integer within a range (inclusive) from 'low' to 'high' diff --git a/README.md b/README.md index a37443f..94aa26c 100644 --- a/README.md +++ b/README.md @@ -77,8 +77,10 @@ currently available in user defined modes. dynamic, block, guided, and static loop scheduling, barriers, master and single execution regions +## Examples and Benchmarks -## Word Counting Using Atomic Operations + +### Word Counting Using Atomic Operations Map-Reduce is often demonstrated using word counting because each document can be processed in parallel, and the results of each document's dictionary reduced into a single dictionary. This EMS implementation also @@ -87,70 +89,6 @@ across processes, atomically incrementing the count of each word found. The final word counts are sorted and the most frequently appearing words are printed with their counts. -### Forming the "Bag of Words" with Word Counts -```javascript -var file_list = fs.readdirSync(doc_path); -var splitPattern = new RegExp(/[ \n,\.\\/_\-\<\>:\;\!\@\#\$\%\&\*\(\)=\[\]|\"\'\{\}\?\—]/); -// Iterate over all the files in parallel -ems.parForEach(0, file_list.length, function (fileNum) { - var text = fs.readFileSync(doc_path + file_list[fileNum], 'utf8', "r"); - var words = text.replace(/[\n\r]/g, ' ').toLowerCase().split(splitPattern); - // Sequentially iterate over all the words in one document - words.forEach(function (word) { - wordCounts.faa(word, 1); // Atomically increment the count of times this word was seen - }); -}); -``` - -### Sorting the Word Count list by Frequency -Parallel sorting of the word count is implemented as a multi-step process: - -1. The bag of words is processed by all the processess, each process - building an ordered list of the most common words it encounters -2. The partial lists from all the processes are concatenated into a single list -3. The list of the most common words seen is sorted and truncated - -```javascript -var biggest_counts = new Array(local_sort_len).fill({"key": 0, "count": 0}); -ems.parForEach(0, maxNKeys, function (keyN) { - var key = wordCounts.index2key(keyN); - if (key) { - // Perform an insertion sort of the new key into the biggest_counts - // list, deleting the last (smallest) element to preserve length. - var keyCount = wordCounts.read(key); - var idx = local_sort_len - 1; - while (idx >= 0 && biggest_counts[idx].count < keyCount) { idx -= 1; } - var newBiggest = {"key": key, "count": keyCount}; - if (idx < 0) { - biggest_counts = [newBiggest].concat(biggest_counts.slice(0, biggest_counts.length - 1)); - } else if (idx < local_sort_len) { - var left = biggest_counts.slice(0, idx + 1); - var right = biggest_counts.slice(idx + 1); - biggest_counts = left.concat([newBiggest].concat(right)).slice(0, -1); - } - } -}); -// Concatenate all the partial (one per process) lists into one list -stats.writeXF('most_frequent', []); // Initialize the list -ems.barrier(); // Wait for all procs to have finished initialization -stats.writeEF('most_frequent', stats.readFE('most_frequent').concat(biggest_counts)); -ems.barrier(); // Wait for all procs to have finished merge -ems.master(function() { // Sort & print the list of words, only one process is needed - biggest_counts = stats.readFF('most_frequent'); - biggest_counts.sort(function (a, b) { return b.count - a.count; }); - // Print only the first "local_sort_len" items -- assume the worst case - // of all the largest counts are discovered by a single process - console.log("Most frequently appearing terms:"); - for (var index = 0; index < local_sort_len; index += 1) { - console.log(index + ': ' + biggest_counts[index].key + " " + biggest_counts[index].count); - } -}); -``` - -### Word Count Performance -This section reports the results of running the Word Count example on -documents from Project Gutenberg. -2,981,712,952 words in several languages were parsed, totaling 12,664,852,220 bytes of text. @@ -171,34 +109,6 @@ floating point operations can be performed on a -## Transactional Memory -Using EMS Transactional Memory to atomically update -two account balances while simultaneously preventing updates to the user's -customer records. - -```javascript -var ems = require('ems')(process.argv[2]) // Initialize EMS -var customers = ems.new(...) // Allocate EMS memory for customer records -var accounts = ems.new(...) // Allocate accounts -... -// Start a transaction involving Bob and Sue -var transaction= ems.tmStart( [ [customers, 'Bob Smith', true], // Read-only: Bob's customer record - [customers, 'Sue Jones', true], // Read-only: Sue's customer record - [accounts, 'Bob Smith'], // Read-Write: Bob's account balance - [accounts, 'Sue Jones'] ] ); // Read-Write: Sue's account balance -// Transfer the payment and update the balances -var bobsBalance = accounts.read('Bob Smith'); // Read the balance of Bob's account -accounts.write('Bob Smith', bobsBalance - paymentAmount); // Deduct the payment and write the new balance back -var suesBalance = accounts.read('Sue Jones'); // Read the balance of Sue's account -accounts.write('Sue Jones', suesBalance + paymentAmount); // Add the payment to Sue's account -// Commit the transaction or abort it if NSF -if(balance > paymentAmount) { // Test for overdraft - ems.tmEnd(transaction, true); // Sufficient funds, commit transaction -} else { - ems.tmEnd(transaction, false); // Not Sufficient Funds, roll back transaction -} -``` - ### Benchmarking of Transactions and Work Queues The micro-benchmarked raw transactional performance and performance in the context of a workload are measured separately. @@ -371,7 +281,7 @@ vary with Linux distribution. Run the work queue driven transaction processing example on 8 processes: ```sh -npm run example +npm run ``` Or manually via: @@ -397,17 +307,17 @@ As of 2016-01-24, Mac/Darwin and Linux are supported. A windows port pull reque ## Roadmap EMS 1.0 uses Nan for long-term Node.js support, we continue to -develop on OSX and Linux via Vagrant. Version 1.x is feature-frozen, -but support continues as we back-patch fixes and featues on our way to version 2.0. +develop on OSX and Linux via Vagrant. + +EMS 1.3 introduces a C API. + +EMS 1.4 **[Planned]** Python API + +EMS 1.5 **[Planned]** Support for [persistent memory](http://pmem.io/). -EMS 2.0 is in the planning phase, the new API will more tightly integrate with +EMS 2.0 **[Planned]** New API with more tightly integrate with ES6, Python, and other dynamically typed languages languages, making atomic operations on persistent memory more transparent. -These new language features also simplify integration with legacy applications. -The types of persistent and Non-Volatile Memory (NVM) EMS was designed -for, such as [pmem.io](http://pmem.io/), -will be introduced into servers in the next few years, we hope to -leverage their work and future-proof EMS against architectural shifts. ## License BSD, other commercial and open source licenses are available. diff --git a/Tests/fj_args.js b/Tests/fj_args.js index 8805091..d5b2f4c 100644 --- a/Tests/fj_args.js +++ b/Tests/fj_args.js @@ -1,5 +1,5 @@ /*-----------------------------------------------------------------------------+ - | Extended Memory Semantics (EMS) Version 1.2.0 | + | Extended Memory Semantics (EMS) Version 1.3.0 | | Synthetic Semantics http://www.synsem.com/ mogill@synsem.com | +-----------------------------------------------------------------------------+ | Copyright (c) 2011-2014, Synthetic Semantics LLC. All rights reserved. | @@ -32,7 +32,7 @@ 'use strict'; var nProcs = parseInt(process.argv[2]); var ems = require('ems')(parseInt(nProcs), true, 'fj'); -var assert; +var assert = require('assert'); var global_str; var check_glob_str; diff --git a/Tests/fullArrays.js b/Tests/fullArrays.js index 733d717..d0cdc42 100644 --- a/Tests/fullArrays.js +++ b/Tests/fullArrays.js @@ -1,5 +1,5 @@ /*-----------------------------------------------------------------------------+ - | Extended Memory Semantics (EMS) Version 1.0.0 | + | Extended Memory Semantics (EMS) Version 1.3.0 | | Synthetic Semantics http://www.synsem.com/ mogill@synsem.com | +-----------------------------------------------------------------------------+ | Copyright (c) 2011-2014, Synthetic Semantics LLC. All rights reserved. | @@ -64,3 +64,10 @@ ems.parForEach(0, arrLen, function (idx) { ems.parForEach(0, arrLen, function (idx) { assert(map.readFF(idx) === 10 * idx, "map readback was wrong"); }); + +try { + map.writeEF('fizz', -9); + assert(false, "Writing one more element should have failed"); +} catch (asd) { + console.log("Correctly detected overflow of mapped EMS array"); +} diff --git a/Tests/mapped_test.js b/Tests/mapped_test.js index 129ece7..34b086a 100644 --- a/Tests/mapped_test.js +++ b/Tests/mapped_test.js @@ -1,5 +1,5 @@ /*-----------------------------------------------------------------------------+ - | Extended Memory Semantics (EMS) Version 1.0.0 | + | Extended Memory Semantics (EMS) Version 1.3.0 | | Synthetic Semantics http://www.synsem.com/ mogill@synsem.com | +-----------------------------------------------------------------------------+ | Copyright (c) 2011-2014, Synthetic Semantics LLC. All rights reserved. | @@ -32,9 +32,8 @@ 'use strict'; var ems = require('ems')(parseInt(process.argv[2]), false); var assert = require('assert'); -var arrLen = 10000; -// var arrayElem = ['abcd', 1234.567, {x: 'xxx', y: 'yyyyy'}, 987, null, [10, 11, 12, 13]]; var arrayElem = ['abcd', 1234.567, true, 987]; +var arrLen = arrayElem.length; var objMap = ems.new({ dimensions: [arrLen], diff --git a/Tests/readers-writer.js b/Tests/readers-writer.js index 65c5beb..8d3441c 100644 --- a/Tests/readers-writer.js +++ b/Tests/readers-writer.js @@ -1,5 +1,5 @@ /*-----------------------------------------------------------------------------+ - | Extended Memory Semantics (EMS) Version 1.0.0 | + | Extended Memory Semantics (EMS) Version 1.3.0 | | Synthetic Semantics http://www.synsem.com/ mogill@synsem.com | +-----------------------------------------------------------------------------+ | Copyright (c) 2011-2014, Synthetic Semantics LLC. All rights reserved. | @@ -84,9 +84,9 @@ ems.barrier(); assert(nLocks <= 7, "Too many locks: " + nLocks); nLocks = count.faa(elem, -1) - 1; assert(nLocks >= 0, "Too few locks: " + nLocks); - for (var i = 0; i < 100; i++) { + for (var i = 0; i < nLocks * 100; i += 1) { nLocks += Math.sin(i); } - nReaders = objMap.releaseRW(elem); + objMap.releaseRW(elem); } }); diff --git a/Tests/strcpy.js b/Tests/strcpy.js index d087b64..41b8263 100644 --- a/Tests/strcpy.js +++ b/Tests/strcpy.js @@ -1,8 +1,39 @@ +/*-----------------------------------------------------------------------------+ + | Extended Memory Semantics (EMS) Version 1.3.0 | + | Synthetic Semantics http://www.synsem.com/ mogill@synsem.com | + +-----------------------------------------------------------------------------+ + | Copyright (c) 2011-2014, Synthetic Semantics LLC. All rights reserved. | + | Copyright (c) 2015-2016, Jace A Mogill. All rights reserved. | + | | + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the following conditions are met: | + | * Redistributions of source code must retain the above copyright | + | notice, this list of conditions and the following disclaimer. | + | * Redistributions in binary form must reproduce the above copyright | + | notice, this list of conditions and the following disclaimer in the | + | documentation and/or other materials provided with the distribution. | + | * Neither the name of the Synthetic Semantics nor the names of its | + | contributors may be used to endorse or promote products derived | + | from this software without specific prior written permission. | + | | + | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | + | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | + | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | + | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SYNTHETIC | + | SEMANTICS LLC BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | + | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | + | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | + | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | + | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | + | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | + | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | + | | + +-----------------------------------------------------------------------------*/ 'use strict'; var ems = require('ems')(parseInt(process.argv[2])); var assert = require('assert'); var fs = require('fs'); -var maxlen = 10000000; +var maxlen = 20000000; var stats = ems.new({ filename: '/tmp/EMS_strlen', dimensions: [10], @@ -16,10 +47,20 @@ var stats = ems.new({ if (ems.myID === 0) { stats.writeXE('test_str', 123); } ems.barrier(); +function stringFill(x, n) { + var s = ''; + for (;;) { + if (n & 1) s += x; + n >>= 1; + if (n) x += x; + else break; + } + return s; +} + for (var len=2; len < maxlen; len = Math.floor(len * 1.5) ) { if (ems.myID === 0) { console.log("Len = " + len); } - var str = ""; - for (var idx = 0; idx < len; idx += 1) { str += "x"; } + var str = stringFill('x', len); stats.writeEF('test_str', str); var readback = stats.readFE('test_str'); assert(readback === str, 'Mismatched string. Expected len ' + str.length + ' got ' + readback.length); diff --git a/Tests/tm_noq.js b/Tests/tm_noq.js index 9e8aad5..196d570 100644 --- a/Tests/tm_noq.js +++ b/Tests/tm_noq.js @@ -1,5 +1,5 @@ /*-----------------------------------------------------------------------------+ - | Extended Memory Semantics (EMS) Version 1.0.0 | + | Extended Memory Semantics (EMS) Version 1.3.0 | | Synthetic Semantics http://www.synsem.com/ mogill@synsem.com | +-----------------------------------------------------------------------------+ | Copyright (c) 2011-2014, Synthetic Semantics LLC. All rights reserved. | @@ -37,14 +37,6 @@ var nTransactions = 400000; var nTables = 6; var maxNops = 5; var tables = []; -/* -var workQ = ems.new({ - dimensions: [nTransactions + ems.nThreads], - heapSize: nTransactions * 20, - useExisting: false, - setFEtags: 'empty' -}); -*/ var totalNops = ems.new(2); var checkNops = ems.new(1); @@ -54,9 +46,7 @@ for (var tableN = 0; tableN < nTables; tableN++) { tables[tableN] = ems.new({ dimensions: [arrLen], heapSize: 0, - // useMap: true, useExisting: false, - // persist: true, filename: '/tmp/EMS_tm' + tableN, dataFill: 0, doDataFill: true, @@ -76,15 +66,12 @@ ems.barrier(); function makeWork() { var ops = []; var nOps = util.randomInRange(1, maxNops); - // var indexes = [] for (var opN = 0; opN < nOps; opN++) { var tableN = util.randomInRange(0, nTables); var idx = util.randomInRange(0, arrLen); if (idx % 10 < 5 || opN % 3 > 0) { ops.push([tableN, idx, true]); - } -// if(opN % 3 > 0) { ops.push([tableN, idx, true]) } - else { + } else { ops.push([tableN, idx]); } } @@ -139,7 +126,6 @@ ems.parForEach(0, nTransactions, function (iter) { }); totalNops.faa(0, rwNops); totalNops.faa(1, readNops); - ems.barrier(); util.timerStop(startTime, nTransactions, " transactions performed ", ems.myID); util.timerStop(startTime, totalNops.readFF(0), " table updates ", ems.myID); diff --git a/Tests/v8Types.js b/Tests/v8Types.js index 778f045..6c50f85 100644 --- a/Tests/v8Types.js +++ b/Tests/v8Types.js @@ -1,5 +1,5 @@ /*-----------------------------------------------------------------------------+ - | Extended Memory Semantics (EMS) Version 1.0.0 | + | Extended Memory Semantics (EMS) Version 1.3.0 | | Synthetic Semantics http://www.synsem.com/ mogill@synsem.com | +-----------------------------------------------------------------------------+ | Copyright (c) 2011-2014, Synthetic Semantics LLC. All rights reserved. | @@ -34,18 +34,8 @@ var ems = require('ems')(parseInt(process.argv[2]), false); var assert = require('assert'); var arrLen = 10000; var a = ems.new(arrLen, arrLen * 400); -/* -var map = ems.new({ - dimensions: [arrLen], - heapSize: arrLen * 10, - useMap: true, - useExisting: false, - setFEtags: 'full', - dataFill: 0 -}) -*/ -var arrayElem = ['abcd', true, 1234.567, {x: 'xxx', y: 'yyyyy'}, 987, null, [10, 11, 12, 13]]; +var arrayElem = ['abcd', true, 1234.567, false, {x: 'xxx', y: 'yyyyy'}, 987, null, [10, 11, 12, 13]]; var objElem = {a: 1, b: 321.653, c: 'asdasd'}; var objMap = ems.new({ @@ -75,13 +65,11 @@ if (ems.myID == 0) { assert(objMap.readFE('any obj').c === objElem.c); arrayElem.forEach(function (elem, idx) { - // console.log(arrObj.readFF(123)[idx], elem, typeof elem); - if (typeof elem == 'object') { - if (typeof arrObj.readFF(123)[idx] != 'object') { - console.log('Object in array is no longer an object?'); - } + if (typeof elem === 'object') { + assert(typeof arrObj.readFF(123)[idx] === 'object'); } else { - assert(arrObj.readFF(123)[idx] === elem); + var readback = arrObj.readFF(123); + assert(readback[idx] === elem); } }); arrObj.readFE(123); @@ -120,7 +108,6 @@ if (ems.myID == 0) { ems.barrier(); - //---------------------------------------- var newIdx, newVal, oldVal, js; @@ -142,24 +129,24 @@ for (var old = 0; old < data.length; old++) { } } + ems.barrier(); var id = (ems.myID + 1) % ems.nThreads; for (var memIdx = 0; memIdx < data.length; memIdx++) { for (var oldIdx = 0; oldIdx < data.length; oldIdx++) { for (newIdx = 0; newIdx < data.length; newIdx++) { a.writeXF(id, data[memIdx]); - oldVal = a.cas(id, data[oldIdx], data[newIdx]); + var memVal = a.cas(id, data[oldIdx], data[newIdx]); newVal = a.readFF(id); - js = data[memIdx]; if (js === data[oldIdx]) { js = data[newIdx]; } assert(js === newVal, - 'CAS: newval=' + newVal + ' dataold=' + data[oldIdx] + - ' datanew=' + data[newIdx] + ' old=' + oldVal + - ' readback=' + newVal + ' js=' + js); + 'CAS: intial=' + data[memIdx] + " memval=" + memVal + + " readback=" + newVal + " oldVal=" + data[oldIdx] + + ' expected=' + data[newIdx] + ' JS:' + js); } } } diff --git a/Vagrantfile b/Vagrantfile index 3c10990..66eeaa1 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -48,7 +48,8 @@ Vagrant.configure(2) do |config| # vb.gui = true # # # Customize the amount of memory on the VM: - # vb.memory = "1024" + # vb.memory = 1024 + # vb.cpus = 1 # end # # View the documentation for the provider you are using for more diff --git a/binding.gyp b/binding.gyp index e2d53b1..9ecaa61 100644 --- a/binding.gyp +++ b/binding.gyp @@ -3,7 +3,7 @@ "targets": [ { "target_name": "ems", - "sources": ["collectives.cc", "ems.cc", "ems_alloc.cc", "loops.cc", "primitives.cc", "rmw.cc"], + "sources": ["collectives.cc", "ems.cc", "ems_alloc.cc", "loops.cc", "nodejs.cc", "primitives.cc", "rmw.cc"], "include_dirs" : [ "& info) { - THIS_INFO_TO_EMSBUF(info, "mmapID"); +bool EMSsingleTask(int mmapID) { + void *emsBuf = emsBufs[mmapID]; int32_t *bufInt32 = (int32_t *) emsBuf; // Increment the tally of threads that have reached this statement @@ -49,59 +49,56 @@ void EMSsingleTask(const Nan::FunctionCallbackInfo& info) { // Return True if this thread was first to the counter, later // threads return false - info.GetReturnValue().Set(Nan::New(retval == 0)); - return; + return retval == 0; } //================================================================== // Critical Region Entry -- 1 thread at a time passes this barrier // -void EMScriticalEnter(const Nan::FunctionCallbackInfo& info) { +int EMScriticalEnter(int mmapID, int timeout) { RESET_NAP_TIME; - THIS_INFO_TO_EMSBUF(info, "mmapID"); + void *emsBuf = emsBufs[mmapID]; int32_t *bufInt32 = (int32_t *) emsBuf; // Acquire the mutual exclusion lock - while (!__sync_bool_compare_and_swap(&(bufInt32[EMS_CB_CRITICAL]), EMS_TAG_FULL, EMS_TAG_EMPTY)) { + while (!__sync_bool_compare_and_swap(&(bufInt32[EMS_CB_CRITICAL]), EMS_TAG_FULL, EMS_TAG_EMPTY) + && timeout > 0 ) { NANOSLEEP; + timeout -= 1; } - info.GetReturnValue().Set(Nan::New(true)); - return; + return timeout; } //================================================================== // Critical Region Exit -void EMScriticalExit(const Nan::FunctionCallbackInfo& info) { - THIS_INFO_TO_EMSBUF(info, "mmapID"); +bool EMScriticalExit(int mmapID) { + void *emsBuf = emsBufs[mmapID]; int32_t *bufInt32 = (int32_t *) emsBuf; // Test the mutual exclusion lock wasn't somehow lost if (bufInt32[EMS_CB_CRITICAL] != EMS_TAG_EMPTY) { - Nan::ThrowError("EMScriticalExit: critical region mutex lost while locked?!"); - return; - - } else - bufInt32[EMS_CB_CRITICAL] = EMS_TAG_FULL; + return false; + } - info.GetReturnValue().Set(Nan::New(true)); - return; + bufInt32[EMS_CB_CRITICAL] = EMS_TAG_FULL; + return true; } //================================================================== // Phase Based Global Thread Barrier -void EMSbarrier(const Nan::FunctionCallbackInfo& info) { - THIS_INFO_TO_EMSBUF(info, "mmapID"); +int EMSbarrier(int mmapID, int timeout) { + void *emsBuf = emsBufs[mmapID]; int32_t *bufInt32 = (int32_t *) emsBuf; int barPhase = bufInt32[EMS_CB_BARPHASE]; // Determine current phase of barrier int retval = __sync_fetch_and_add(&bufInt32[EMS_CB_NBAR0 + barPhase], -1); if (retval < 0) { - Nan::ThrowError("EMSbarrier: Race condition at barrier"); - return; + fprintf(stderr, "EMSbarrier: Race condition at barrier\n"); + return false; } if (retval == 1) { @@ -112,8 +109,11 @@ void EMSbarrier(const Nan::FunctionCallbackInfo& info) { } else { // Wait for the barrier phase to change, indicating the last thread arrived RESET_NAP_TIME; - while (barPhase == bufInt32[EMS_CB_BARPHASE]) {NANOSLEEP; } + while (timeout > 0 && barPhase == bufInt32[EMS_CB_BARPHASE]) { + NANOSLEEP; + timeout -= 1; + } } - return; + return timeout; } diff --git a/ems.cc b/ems.cc index c7b020f..9eb666c 100644 --- a/ems.cc +++ b/ems.cc @@ -1,5 +1,5 @@ /*-----------------------------------------------------------------------------+ - | Extended Memory Semantics (EMS) Version 1.2.0 | + | Extended Memory Semantics (EMS) Version 1.3.0 | | Synthetic Semantics http://www.synsem.com/ mogill@synsem.com | +-----------------------------------------------------------------------------+ | Copyright (c) 2011-2014, Synthetic Semantics LLC. All rights reserved. | @@ -78,6 +78,96 @@ void emsMutexMem_free(struct emsMem *heap, // Base of EMS malloc structs + +//================================================================== +// Convert any type of key to an index +// +int64_t EMSkey2index(void *emsBuf, EMSvalueType *key, bool is_mapped) { + volatile EMStag_t *bufTags = (EMStag_t *) emsBuf; + volatile int64_t *bufInt64 = (int64_t *) emsBuf; + volatile double *bufDouble = (double *) emsBuf; + const char *bufChar = (char *) emsBuf; + + int64_t idx = 0; + switch (key->type) { + case EMS_TYPE_BOOLEAN: + if ((bool) key->value) idx = 1; + else idx = 0; + break; + case EMS_TYPE_INTEGER: + idx = (int64_t) key->value; + break; + case EMS_TYPE_FLOAT: + idx = (int64_t) key->value; + break; + case EMS_TYPE_STRING: + idx = EMShashString((char *) key->value); + break; + case EMS_TYPE_UNDEFINED: + fprintf(stderr, "EMS ERROR: EMSkey2index keyType is defined as Undefined\n"); + return -1; + default: + fprintf(stderr, "EMS ERROR: EMSkey2index keyType(%d) is unknown\n", key->type); + return -1; + } + + int nTries = 0; + bool matched = false; + bool notPresent = false; + EMStag_t mapTags; + if (is_mapped) { + while (nTries < MAX_OPEN_HASH_STEPS && !matched && !notPresent) { + idx = idx % bufInt64[EMScbData(EMS_ARR_NELEM)]; + // Wait until the map key is FULL, mark it busy while map lookup is performed + mapTags.byte = EMStransitionFEtag(&bufTags[EMSmapTag(idx)], NULL, EMS_TAG_FULL, EMS_TAG_BUSY, EMS_TAG_ANY); + if (mapTags.tags.type == key->type) { + switch (key->type) { + case EMS_TYPE_BOOLEAN: + case EMS_TYPE_INTEGER: + if ((int64_t) key->value == bufInt64[EMSmapData(idx)]) { + matched = true; + } + break; + case EMS_TYPE_FLOAT: { + ulong_double alias; + alias.u64 = (uint64_t) key->value; + if (alias.d == bufDouble[EMSmapData(idx)]) { + matched = true; + } + } + break; + case EMS_TYPE_STRING: { + int64_t keyStrOffset = bufInt64[EMSmapData(idx)]; + if (strcmp((const char *) key->value, EMSheapPtr(keyStrOffset)) == 0) { + matched = true; + } + } + break; + case EMS_TYPE_UNDEFINED: + // Nothing hashed to this map index yet, so the key does not exist + notPresent = true; + break; + default: + fprintf(stderr, "EMS ERROR: EMSreadIndexMap: Unknown mem type\n"); + matched = true; + } + } + if (mapTags.tags.type == EMS_TYPE_UNDEFINED) notPresent = true; + bufTags[EMSmapTag(idx)].tags.fe = EMS_TAG_FULL; + if (!matched) { + // No match, set this Map entry back to full and try again + nTries++; + idx++; + } + } + if (!matched) { idx = -1; } + } + + return idx % bufInt64[EMScbData(EMS_ARR_NELEM)]; +} + + + #if 0 //================================================================== // Callback for destruction of an EMS array @@ -158,127 +248,6 @@ int64_t EMShashString(const char *key) { } -//================================================================== -// Find the matching map key for this argument. -// Returns the index of the element, or -1 for no match. -// The stored Map key value is read when full and marked busy. -// If the data does not match, it is marked full again, but if -// there is a match the map key is kept busy until the operation -// on the data is complete. -// -int64_t EMSreadIndexMap(const Nan::FunctionCallbackInfo& info) { - THIS_INFO_TO_EMSBUF(info, "mmapID"); - int64_t idx = 0; - volatile int64_t *bufInt64 = (int64_t *) emsBuf; - char *bufChar = emsBuf; - volatile EMStag_t *bufTags = (EMStag_t *) emsBuf; - EMStag_t mapTags; - volatile double *bufDouble = (double *) emsBuf; - int idxType = EMSv8toEMStype(info[0], false); - int64_t boolArgVal = false; - int64_t intArgVal = -1; - double floatArgVal = 0.0; - int nTries = 0; - int matched = false; - int notPresent = false; - const char *arg_c_str = NULL; // Assignment needed to quiet GCC uninitalized warning - std::string argString; - - if (info.Length() == 0) { - Nan::ThrowTypeError("EMS ERROR: EMSreadIndexMap has no arguments?"); - return -1; - } - - switch (idxType) { - case EMS_TYPE_BOOLEAN: - boolArgVal = (info[0]->ToBoolean()->Value() != 0); - idx = boolArgVal; - break; - case EMS_TYPE_INTEGER: - intArgVal = info[0]->ToInteger()->Value(); - idx = intArgVal; - break; - case EMS_TYPE_FLOAT: - ulong_double alias; - alias.d = info[0]->ToNumber()->Value(); - idx = alias.u; - break; - case EMS_TYPE_STRING: - argString = std::string(*Nan::Utf8String(info[0])); - arg_c_str = argString.c_str(); - idx = EMShashString(arg_c_str); - break; - default: - Nan::ThrowTypeError("EMS ERROR: EMSreadIndexMap Unknown arg type"); - return -1; - } - - if (EMSisMapped) { - while (nTries < MAX_OPEN_HASH_STEPS && !matched && !notPresent) { - idx = idx % bufInt64[EMScbData(EMS_ARR_NELEM)]; - // Wait until the map key is FULL, mark it busy while map lookup is performed - mapTags.byte = EMStransitionFEtag(&bufTags[EMSmapTag(idx)], NULL, EMS_TAG_FULL, EMS_TAG_BUSY, EMS_TAG_ANY); - if (mapTags.tags.type == idxType) { - switch (idxType) { - case EMS_TYPE_BOOLEAN: - if (boolArgVal == (bufInt64[EMSmapData(idx)] != 0)) { - matched = true; - bufTags[EMSmapTag(idx)].tags.fe = EMS_TAG_FULL; - } - break; - case EMS_TYPE_INTEGER: - if (intArgVal == bufInt64[EMSmapData(idx)]) { - matched = true; - bufTags[EMSmapTag(idx)].tags.fe = EMS_TAG_FULL; - } - break; - case EMS_TYPE_FLOAT: - if (floatArgVal == bufDouble[EMSmapData(idx)]) { - matched = true; - bufTags[EMSmapTag(idx)].tags.fe = EMS_TAG_FULL; - } - break; - case EMS_TYPE_STRING: { - int64_t keyStrOffset = bufInt64[EMSmapData(idx)]; - if (strcmp(arg_c_str, EMSheapPtr(keyStrOffset)) == 0) { - matched = true; - bufTags[EMSmapTag(idx)].tags.fe = EMS_TAG_FULL; - } - } - break; - case EMS_TYPE_UNDEFINED: - // Nothing hashed to this map index yet, so the key does not exist - notPresent = true; - bufTags[EMSmapTag(idx)].tags.fe = EMS_TAG_FULL; - break; - default: - fprintf(stderr, "EMS ERROR: EMSreadIndexMap: Unknown mem type\n"); - matched = true; - } - } - if (mapTags.tags.type == EMS_TYPE_UNDEFINED) notPresent = true; - if (!matched) { - // No match, set this Map entry back to full and try again - bufTags[EMSmapTag(idx)].tags.fe = EMS_TAG_FULL; - nTries++; - idx++; - } - } - if (!matched) { idx = -1; } - } else { // Wasn't mapped, do bounds check - if (idx < 0 || idx >= bufInt64[EMScbData(EMS_ARR_NELEM)]) { - idx = -1; - } - } - - if (nTries >= MAX_OPEN_HASH_STEPS) { - fprintf(stderr, "EMSreadIndexMap ran out of key mappings\n"); - } - if (notPresent) idx = -1; - return idx; -} - - //================================================================== // Find the matching map key, if not present, find the // next available open address. @@ -287,51 +256,21 @@ int64_t EMSreadIndexMap(const Nan::FunctionCallbackInfo& info) { // match, the map key is left empty and this function // returns the index of an existing or available array element. // -int64_t EMSwriteIndexMap(const Nan::FunctionCallbackInfo& info) { - THIS_INFO_TO_EMSBUF(info, "mmapID"); - - int64_t idx = 0; - volatile int64_t *bufInt64 = (int64_t *) emsBuf; - char *bufChar = (char *) emsBuf; - volatile EMStag_t *bufTags = (EMStag_t *) emsBuf; +int64_t EMSwriteIndexMap(const int mmapID, EMSvalueType *key) { + char *emsBuf = emsBufs[mmapID]; + volatile int64_t *bufInt64 = (int64_t *) emsBuf; + volatile char *bufChar = (char *) emsBuf; + volatile EMStag_t *bufTags = (EMStag_t *) emsBuf; + volatile double *bufDouble = (double *) emsBuf; EMStag_t mapTags; - volatile double *bufDouble = (double *) emsBuf; - unsigned char idxType = EMSv8toEMStype(info[0], false); - int64_t boolArgVal = false; - int64_t intArgVal = -1; - double floatArgVal = 0.0; - std::string argString(*Nan::Utf8String(info[0])); - const char *arg_c_str = argString.c_str(); - - if (info.Length() == 0) { - fprintf(stderr, "EMS ERROR: EMSwriteIndexMap has no arguments?\n"); - return (-1); - } - switch (idxType) { - case EMS_TYPE_BOOLEAN: - boolArgVal = (info[0]->ToBoolean()->Value() != 0); - idx = boolArgVal; - break; - case EMS_TYPE_INTEGER: - intArgVal = info[0]->ToInteger()->Value(); - idx = intArgVal; - break; - case EMS_TYPE_FLOAT: - ulong_double alias; - alias.d = info[0]->ToNumber()->Value(); - idx = alias.u; - break; - case EMS_TYPE_STRING: - idx = EMShashString(arg_c_str); - break; - case EMS_TYPE_UNDEFINED: - Nan::ThrowTypeError("EMSwriteIndexMap: Undefined is not a valid index"); - return (-1); - default: - fprintf(stderr, "EMS ERROR: EMSwriteIndexMap: Unknown mem type\n"); - return (-1); + // If the key already exists, use it + int64_t idx = EMSkey2index(emsBuf, key, EMSisMapped); + if(idx > 0) { + // fprintf(stderr, "write index map -- key already existed\n"); + return idx; } + idx = EMSkey2index(emsBuf, key, false); int nTries = 0; if (EMSisMapped) { @@ -341,53 +280,60 @@ int64_t EMSwriteIndexMap(const Nan::FunctionCallbackInfo& info) { // Wait until the map key is FULL, mark it busy while map lookup is performed mapTags.byte = EMStransitionFEtag(&bufTags[EMSmapTag(idx)], NULL, EMS_TAG_FULL, EMS_TAG_BUSY, EMS_TAG_ANY); mapTags.tags.fe = EMS_TAG_FULL; // When written back, mark FULL - if (mapTags.tags.type == idxType || mapTags.tags.type == EMS_TYPE_UNDEFINED) { + if (mapTags.tags.type == key->type || mapTags.tags.type == EMS_TYPE_UNDEFINED) { switch (mapTags.tags.type) { case EMS_TYPE_BOOLEAN: - if (boolArgVal == (bufInt64[EMSmapData(idx)] != 0)) { + if ((int64_t) key->value == (bufInt64[EMSmapData(idx)] != 0)) { matched = true; bufTags[EMSmapTag(idx)].tags.fe = EMS_TAG_FULL; } break; case EMS_TYPE_INTEGER: - if (intArgVal == bufInt64[EMSmapData(idx)]) { + if ((int64_t) key->value == bufInt64[EMSmapData(idx)]) { matched = true; bufTags[EMSmapTag(idx)].tags.fe = EMS_TAG_FULL; } break; - case EMS_TYPE_FLOAT: - if (floatArgVal == bufDouble[EMSmapData(idx)]) { + case EMS_TYPE_FLOAT: { + ulong_double alias; + alias.u64 = (uint64_t) key->value; + if (alias.d == bufDouble[EMSmapData(idx)]) { matched = true; bufTags[EMSmapTag(idx)].tags.fe = EMS_TAG_FULL; } + } break; case EMS_TYPE_STRING: { int64_t keyStrOffset = bufInt64[EMSmapData(idx)]; - if (strcmp(arg_c_str, EMSheapPtr(keyStrOffset)) == 0) { + if (strcmp((const char *) key->value, (const char *) EMSheapPtr(keyStrOffset)) == 0) { matched = true; bufTags[EMSmapTag(idx)].tags.fe = EMS_TAG_FULL; } } break; case EMS_TYPE_UNDEFINED: - // This map key index is still unused, so there was no match. - // Instead, allocate this element - bufTags[EMSmapTag(idx)].tags.type = idxType; - switch (idxType) { + // This map key index is still unused, so there was no match and a new + // mapped element must be allocated to perform the tag bit transitions upon + bufTags[EMSmapTag(idx)].tags.type = key->type; + switch (key->type) { case EMS_TYPE_BOOLEAN: - bufInt64[EMSmapData(idx)] = boolArgVal; + bufInt64[EMSmapData(idx)] = ((int64_t) key->value != 0); break; case EMS_TYPE_INTEGER: - bufInt64[EMSmapData(idx)] = intArgVal; + bufInt64[EMSmapData(idx)] = (int64_t) key->value; break; - case EMS_TYPE_FLOAT: - bufDouble[EMSmapData(idx)] = floatArgVal; + case EMS_TYPE_FLOAT: { + ulong_double alias; + alias.u64 = (uint64_t) key->value; + bufDouble[EMSmapData(idx)] = alias.d; + } break; case EMS_TYPE_STRING: { int64_t textOffset; - EMS_ALLOC(textOffset, argString.length() + 1, "EMSwriteIndexMap(string): out of memory to store string", -1); + EMS_ALLOC(textOffset, strlen((const char *) key->value) + 1, bufChar, + "EMSwriteIndexMap(string): out of memory to store string", -1); bufInt64[EMSmapData(idx)] = textOffset; - strcpy(EMSheapPtr(textOffset), arg_c_str); + strcpy((char *) EMSheapPtr(textOffset), (const char *) key->value); } break; case EMS_TYPE_UNDEFINED: @@ -399,7 +345,7 @@ int64_t EMSwriteIndexMap(const Nan::FunctionCallbackInfo& info) { matched = true; break; default: - fprintf(stderr, "EMS ERROR: EMSwriteIndexMap: Unknown mem type\n"); + fprintf(stderr, "EMS ERROR: EMSwriteIndexMap: Unknown tag type (%d) on map key\n", mapTags.tags.type); matched = true; } } @@ -413,13 +359,14 @@ int64_t EMSwriteIndexMap(const Nan::FunctionCallbackInfo& info) { } } else { // Wasn't mapped, do bounds check if (idx < 0 || idx >= bufInt64[EMScbData(EMS_ARR_NELEM)]) { + fprintf(stderr, "Wasn't mapped do bounds check\n"); idx = -1; } } if (nTries >= MAX_OPEN_HASH_STEPS) { idx = -1; - fprintf(stderr, "EMSwriteIndexMap ran out of key mappings (returning %" PRIu64 ")\n", idx); + fprintf(stderr, "EMSwriteIndexMap ran out of key mappings (ntries=%d)\n", nTries); } return idx; @@ -427,48 +374,46 @@ int64_t EMSwriteIndexMap(const Nan::FunctionCallbackInfo& info) { - //================================================================== // Read EMS memory, enforcing Full/Empty tag transitions -// -void EMSreadUsingTags(const Nan::FunctionCallbackInfo& info, // Index to read from +bool EMSreadUsingTags(const int mmapID, + EMSvalueType *key, // Index to read from + EMSvalueType *returnValue, unsigned char initialFE, // Block until F/E tags are this value unsigned char finalFE) // Set the tag to this value when done { RESET_NAP_TIME; - THIS_INFO_TO_EMSBUF(info, "mmapID"); + void *emsBuf = emsBufs[mmapID]; volatile EMStag_t *bufTags = (EMStag_t *) emsBuf; volatile int64_t *bufInt64 = (int64_t *) emsBuf; volatile double *bufDouble = (double *) emsBuf; const char *bufChar = (const char *) emsBuf; - EMStag_t newTag, oldTag, memTag; - if (info.Length() < 1 || info.Length() > 2) { - Nan::ThrowError("EMSreadUsingTags: Wrong number of args"); - return; - } + returnValue->type = EMS_TYPE_UNDEFINED; + returnValue->value = (void *) 0xdeafbeef; // TODO: Should return default value even when not doing write allocate + + EMStag_t newTag, oldTag, memTag; + int64_t idx = EMSkey2index(emsBuf, key, EMSisMapped); - int64_t idx = EMSreadIndexMap(info); // Allocate on Write, writes include modification of the tag: // If the EMS object being read is undefined and we're changing the f/e state // then allocate the undefined object and set the state. If the state is // not changing, do not allocate the undefined element. - if(EMSisMapped && idx < 0 && finalFE != EMS_TAG_ANY) { - idx = EMSwriteIndexMap(info); - if(idx < 0){ - Nan::ThrowError("EMSreadUsingTags: Unable to allocate on read for new map index"); - return; + if(EMSisMapped && idx < 0) { + if (finalFE != EMS_TAG_ANY) { + idx = EMSwriteIndexMap(mmapID, key); + if (idx < 0) { + fprintf(stderr, "EMSreadUsingTags: Unable to allocate on read for new map index\n"); + return false; + } + } else { + return true; } } if (idx < 0 || idx >= bufInt64[EMScbData(EMS_ARR_NELEM)]) { - if (EMSisMapped) { - info.GetReturnValue().Set(Nan::Undefined()); - return; - } else { - Nan::ThrowError("EMSreadUsingTags: index out of bounds"); - return; - } + fprintf(stderr, "EMSreadUsingTags: index out of bounds\n"); + return false; } while (true) { @@ -477,9 +422,9 @@ void EMSreadUsingTags(const Nan::FunctionCallbackInfo& info, // Index if (initialFE == EMS_TAG_ANY || (initialFE != EMS_TAG_RW_LOCK && memTag.tags.fe == initialFE) || (initialFE == EMS_TAG_RW_LOCK && - ((memTag.tags.fe == EMS_TAG_RW_LOCK && newTag.tags.rw < EMS_RW_NREADERS_MAX) || memTag.tags.fe == - EMS_TAG_FULL) && - (memTag.tags.rw < ((1 << EMS_TYPE_NBITS_RW) - 1))// Counter is already saturated + ((memTag.tags.fe == EMS_TAG_RW_LOCK && newTag.tags.rw < EMS_RW_NREADERS_MAX) || + memTag.tags.fe == EMS_TAG_FULL) && + (memTag.tags.rw < ((1 << EMS_TYPE_NBITS_RW) - 1)) // Counter is already saturated ) ) { newTag.byte = memTag.byte; @@ -496,52 +441,44 @@ void EMSreadUsingTags(const Nan::FunctionCallbackInfo& info, // Index // Under BUSY lock: // Read the data, then reset the FE tag, then return the original value in memory newTag.tags.fe = finalFE; + returnValue->type = newTag.tags.type; switch (newTag.tags.type) { case EMS_TYPE_BOOLEAN: { - bool retBool = bufInt64[EMSdataData(idx)]; + returnValue->value = (void *) (bufInt64[EMSdataData(idx)] != 0); if (finalFE != EMS_TAG_ANY) bufTags[EMSdataTag(idx)].byte = newTag.byte; if (EMSisMapped) bufTags[EMSmapTag(idx)].tags.fe = EMS_TAG_FULL; - info.GetReturnValue().Set(Nan::New(retBool)); - return; + return true; } case EMS_TYPE_INTEGER: { - int32_t retInt = bufInt64[EMSdataData(idx)]; // TODO: Bug -- only 32 bits of 64? + returnValue->value = (void *) bufInt64[EMSdataData(idx)]; if (finalFE != EMS_TAG_ANY) bufTags[EMSdataTag(idx)].byte = newTag.byte; if (EMSisMapped) bufTags[EMSmapTag(idx)].tags.fe = EMS_TAG_FULL; - info.GetReturnValue().Set(Nan::New(retInt)); - return; + return true; } case EMS_TYPE_FLOAT: { - double retFloat = bufDouble[EMSdataData(idx)]; + ulong_double alias; + alias.d = bufDouble[EMSdataData(idx)]; + returnValue->value = (void *) alias.u64; if (finalFE != EMS_TAG_ANY) bufTags[EMSdataTag(idx)].byte = newTag.byte; if (EMSisMapped) bufTags[EMSmapTag(idx)].tags.fe = EMS_TAG_FULL; - info.GetReturnValue().Set(Nan::New(retFloat)); - return; + return true; } case EMS_TYPE_JSON: case EMS_TYPE_STRING: { + returnValue->value = (void *) EMSheapPtr(bufInt64[EMSdataData(idx)]); if (finalFE != EMS_TAG_ANY) bufTags[EMSdataTag(idx)].byte = newTag.byte; if (EMSisMapped) bufTags[EMSmapTag(idx)].tags.fe = EMS_TAG_FULL; - if (newTag.tags.type == EMS_TYPE_JSON) { - v8::Local retObj = Nan::New(); - retObj->Set(Nan::New("data").ToLocalChecked(), - Nan::New(EMSheapPtr(bufInt64[EMSdataData(idx)])).ToLocalChecked() ); - info.GetReturnValue().Set(retObj); - return; - } else { - info.GetReturnValue().Set(Nan::New(EMSheapPtr(bufInt64[EMSdataData(idx)])).ToLocalChecked()); - return; - } + return true; } case EMS_TYPE_UNDEFINED: { + returnValue->value = (void *) 0xcafebeef; if (finalFE != EMS_TAG_ANY) bufTags[EMSdataTag(idx)].byte = newTag.byte; if (EMSisMapped) bufTags[EMSmapTag(idx)].tags.fe = EMS_TAG_FULL; - info.GetReturnValue().Set(Nan::Undefined()); - return; + return true; } default: - Nan::ThrowError("EMSreadFE unknown type"); - return; + fprintf(stderr, "EMSreadUsingTags: unknown type (%d) read from memory\n", newTag.tags.type); + return false; } } else { // Tag was marked BUSY between test read and CAS, must retry @@ -562,118 +499,100 @@ void EMSreadUsingTags(const Nan::FunctionCallbackInfo& info, // Index //================================================================== // Read under multiple readers-single writer lock -void EMSreadRW(const Nan::FunctionCallbackInfo& info) { - EMSreadUsingTags(info, EMS_TAG_RW_LOCK, EMS_TAG_RW_LOCK); - return; +bool EMSreadRW(const int mmapID, EMSvalueType *key, EMSvalueType *returnValue) { + return EMSreadUsingTags(mmapID, key, returnValue, EMS_TAG_RW_LOCK, EMS_TAG_RW_LOCK); } //================================================================== // Read when full and leave empty -void EMSreadFE(const Nan::FunctionCallbackInfo& info) { - EMSreadUsingTags(info, EMS_TAG_FULL, EMS_TAG_EMPTY); - return; +bool EMSreadFE(const int mmapID, EMSvalueType *key, EMSvalueType *returnValue) { + return EMSreadUsingTags(mmapID, key, returnValue, EMS_TAG_FULL, EMS_TAG_EMPTY); } //================================================================== // Read when full and leave Full -void EMSreadFF(const Nan::FunctionCallbackInfo& info) { - EMSreadUsingTags(info, EMS_TAG_FULL, EMS_TAG_FULL); - return; +bool EMSreadFF(const int mmapID, EMSvalueType *key, EMSvalueType *returnValue) { + return EMSreadUsingTags(mmapID, key, returnValue, EMS_TAG_FULL, EMS_TAG_FULL); } //================================================================== -// Wrapper around read from an EMS array -- first determine the type -void EMSread(const Nan::FunctionCallbackInfo& info) { - EMSreadUsingTags(info, EMS_TAG_ANY, EMS_TAG_ANY); - return; +// Wrapper around read +bool EMSread(const int mmapID, EMSvalueType *key, EMSvalueType *returnValue) { + return EMSreadUsingTags(mmapID, key, returnValue, EMS_TAG_ANY, EMS_TAG_ANY); } //================================================================== -// Decrement the reference counte of the multiple readers-single writer lock -// -void EMSreleaseRW(const Nan::FunctionCallbackInfo& info) { +// Decrement the reference count of the multiple readers-single writer lock +int EMSreleaseRW(const int mmapID, EMSvalueType *key) { RESET_NAP_TIME; - THIS_INFO_TO_EMSBUF(info, "mmapID"); + void *emsBuf = emsBufs[mmapID]; volatile int64_t *bufInt64 = (int64_t *) emsBuf; volatile EMStag_t *bufTags = (EMStag_t *) emsBuf; EMStag_t newTag, oldTag; - if (info.Length() == 1) { - int64_t idx = EMSreadIndexMap(info); - if (idx < 0 || idx >= bufInt64[EMScbData(EMS_ARR_NELEM)]) { - Nan::ThrowError("EMSreleaseRW: invalid index"); - return; - } - while (true) { - oldTag.byte = bufTags[EMSdataTag(idx)].byte; - newTag.byte = oldTag.byte; - if (oldTag.tags.fe == EMS_TAG_RW_LOCK) { - // Already under a RW lock - if (oldTag.tags.rw == 0) { - // Assert the RW count is consistent with the lock state - Nan::ThrowError("EMSreleaseRW: locked but Count already 0"); - return; - } else { - // Decrement the RW reference count - newTag.tags.rw--; - // If this is the last reader, set the FE tag back to full - if (newTag.tags.rw == 0) { newTag.tags.fe = EMS_TAG_FULL; } - // Attempt to commit the RW reference count & FE tag - if (__sync_bool_compare_and_swap(&(bufTags[EMSdataTag(idx)].byte), oldTag.byte, newTag.byte)) { - info.GetReturnValue().Set(Nan::New(newTag.tags.rw)); - return; - } else { - // Another thread decremented the RW count while we also tried - } - } + int64_t idx = EMSkey2index(emsBuf, key, EMSisMapped); + if (idx < 0 || idx >= bufInt64[EMScbData(EMS_ARR_NELEM)]) { + fprintf(stderr, "EMSreleaseRW: invalid index (%" PRIi64 ")\n", idx); + return -1; + } + + while (true) { + oldTag.byte = bufTags[EMSdataTag(idx)].byte; + newTag.byte = oldTag.byte; + if (oldTag.tags.fe == EMS_TAG_RW_LOCK) { + // Already under a RW lock + if (oldTag.tags.rw == 0) { + // Assert the RW count is consistent with the lock state + fprintf(stderr, "EMSreleaseRW: locked but Count already 0\n"); + return -1; } else { - if (oldTag.tags.fe != EMS_TAG_BUSY) { - // Assert the RW lock being release is not in some other state then RW_LOCK or BUSY - Nan::ThrowError("EMSreleaseRW: Lost RW lock? Not locked or busy"); - return; + // Decrement the RW reference count + newTag.tags.rw--; + // If this is the last reader, set the FE tag back to full + if (newTag.tags.rw == 0) { newTag.tags.fe = EMS_TAG_FULL; } + // Attempt to commit the RW reference count & FE tag + if (__sync_bool_compare_and_swap(&(bufTags[EMSdataTag(idx)].byte), oldTag.byte, newTag.byte)) { + return (int) newTag.tags.rw; + } else { + // Another thread decremented the RW count while we also tried } } - // Failed to update the RW count, sleep and retry - NANOSLEEP; + } else { + if (oldTag.tags.fe != EMS_TAG_BUSY) { + // Assert the RW lock being release is not in some other state then RW_LOCK or BUSY + fprintf(stderr, "EMSreleaseRW: The RW lock being released is in some other state then RW_LOCK or BUSY\n"); + return -1; + } } - } else { - Nan::ThrowError("EMSreleaseRW: Wrong number of arguments"); - return; + // Failed to update the RW count, sleep and retry + NANOSLEEP; } } //================================================================== // Write EMS honoring the F/E tags -// -void EMSwriteUsingTags(const Nan::FunctionCallbackInfo& info, // Index to read from +bool EMSwriteUsingTags(int mmapID, + EMSvalueType *key, + EMSvalueType *value, unsigned char initialFE, // Block until F/E tags are this value unsigned char finalFE) // Set the tag to this value when done { RESET_NAP_TIME; - THIS_INFO_TO_EMSBUF(info, "mmapID"); - int64_t idx = EMSwriteIndexMap(info); + char *emsBuf = emsBufs[mmapID]; volatile EMStag_t *bufTags = (EMStag_t *) emsBuf; volatile int64_t *bufInt64 = (int64_t *) emsBuf; volatile double *bufDouble = (double *) emsBuf; char *bufChar = emsBuf; EMStag_t newTag, oldTag, memTag; - int stringIsJSON = false; - if (info.Length() == 3) { - stringIsJSON = info[2]->ToBoolean()->Value(); - } else { - if (info.Length() != 2) { - Nan::ThrowError("EMSwriteUsingTags: Wrong number of args"); - return; - } - } + int64_t idx = EMSwriteIndexMap(mmapID, key); if (idx < 0) { - Nan::ThrowError("EMSwriteUsingTags: index out of bounds"); - return; + fprintf(stderr, "EMSwriteUsingTags: index out of bounds\n"); + return false; } // Wait for the memory to be in the initial F/E state and transition to Busy @@ -702,31 +621,34 @@ void EMSwriteUsingTags(const Nan::FunctionCallbackInfo& info, // Ind } // Store argument value into EMS memory - switch (EMSv8toEMStype(info[1], stringIsJSON)) { + switch (value->type) { case EMS_TYPE_BOOLEAN: - bufInt64[EMSdataData(idx)] = info[1]->ToBoolean()->Value(); + bufInt64[EMSdataData(idx)] = (int64_t) value->value; break; case EMS_TYPE_INTEGER: - bufInt64[EMSdataData(idx)] = (int64_t) info[1]->ToInteger()->Value(); + bufInt64[EMSdataData(idx)] = (int64_t) value->value; break; - case EMS_TYPE_FLOAT: - bufDouble[EMSdataData(idx)] = info[1]->ToNumber()->Value(); + case EMS_TYPE_FLOAT: { + ulong_double alias; + alias.u64 = (uint64_t) value->value; + bufDouble[EMSdataData(idx)] = alias.d; + } break; case EMS_TYPE_JSON: case EMS_TYPE_STRING: { - std::string json(*Nan::Utf8String(info[1])); int64_t textOffset; - EMS_ALLOC(textOffset, json.length() + 1, "EMSwriteUsingTags: out of memory to store string", ); + EMS_ALLOC(textOffset, strlen((const char *) value->value) + 1, bufChar, // NULL padding at end + "EMSwriteUsingTags: out of memory to store string", false); bufInt64[EMSdataData(idx)] = textOffset; - strcpy(EMSheapPtr(textOffset), json.c_str()); + strcpy(EMSheapPtr(textOffset), (const char *) value->value); } break; case EMS_TYPE_UNDEFINED: bufInt64[EMSdataData(idx)] = 0xdeadbeef; break; default: - Nan::ThrowError("EMSwriteUsingTags: Unknown arg type"); - return; + fprintf(stderr, "EMSwriteUsingTags: Unknown arg type\n"); + return false; } oldTag.byte = newTag.byte; @@ -734,17 +656,16 @@ void EMSwriteUsingTags(const Nan::FunctionCallbackInfo& info, // Ind newTag.tags.fe = finalFE; newTag.tags.rw = 0; } - newTag.tags.type = EMSv8toEMStype(info[1], stringIsJSON); + newTag.tags.type = value->type; if (finalFE != EMS_TAG_ANY && bufTags[EMSdataTag(idx)].byte != oldTag.byte) { - Nan::ThrowError("EMSwriteUsingTags: Lost tag lock while BUSY"); - return; + fprintf(stderr, "EMSwriteUsingTags: Lost tag lock while BUSY\n"); + return false; } // Set the tags for the data (and map, if used) back to full to finish the operation bufTags[EMSdataTag(idx)].byte = newTag.byte; if (EMSisMapped) bufTags[EMSmapTag(idx)].tags.fe = EMS_TAG_FULL; - info.GetReturnValue().Set(Nan::True()); - return; + return true; } else { // Tag was marked BUSY between test read and CAS, must retry } @@ -759,226 +680,156 @@ void EMSwriteUsingTags(const Nan::FunctionCallbackInfo& info, // Ind //================================================================== // WriteXF -void EMSwriteXF(const Nan::FunctionCallbackInfo& info) { - EMSwriteUsingTags(info, EMS_TAG_ANY, EMS_TAG_FULL); - return; +bool EMSwriteXF(int mmapID, EMSvalueType *key, EMSvalueType *value) { + return EMSwriteUsingTags(mmapID, key, value, EMS_TAG_ANY, EMS_TAG_FULL); } //================================================================== // WriteXE -void EMSwriteXE(const Nan::FunctionCallbackInfo& info) { - EMSwriteUsingTags(info, EMS_TAG_ANY, EMS_TAG_EMPTY); - return; +bool EMSwriteXE(int mmapID, EMSvalueType *key, EMSvalueType *value) { + return EMSwriteUsingTags(mmapID, key, value, EMS_TAG_ANY, EMS_TAG_EMPTY); } //================================================================== // WriteEF -void EMSwriteEF(const Nan::FunctionCallbackInfo& info) { - EMSwriteUsingTags(info, EMS_TAG_EMPTY, EMS_TAG_FULL); - return; +bool EMSwriteEF(int mmapID, EMSvalueType *key, EMSvalueType *value) { + return EMSwriteUsingTags(mmapID, key, value, EMS_TAG_EMPTY, EMS_TAG_FULL); } //================================================================== // Write -void EMSwrite(const Nan::FunctionCallbackInfo& info) { - EMSwriteUsingTags(info, EMS_TAG_ANY, EMS_TAG_ANY); - return; +bool EMSwrite(int mmapID, EMSvalueType *key, EMSvalueType *value) { + return EMSwriteUsingTags(mmapID, key, value, EMS_TAG_ANY, EMS_TAG_ANY); } //================================================================== // Set only the Full/Empty tag from JavaScript // without inspecting or modifying the data. -// -void EMSsetTag(const Nan::FunctionCallbackInfo& info) { - THIS_INFO_TO_EMSBUF(info, "mmapID"); +bool EMSsetTag(int mmapID, EMSvalueType *key, bool is_full) { + void *emsBuf = emsBufs[mmapID]; volatile EMStag_t *bufTags = (EMStag_t *) emsBuf; + volatile int64_t *bufInt64 = (int64_t *) emsBuf; EMStag_t tag; - int64_t idx = info[0]->ToInteger()->Value(); + + int64_t idx = EMSkey2index(emsBuf, key, EMSisMapped); + if (idx < 0 || idx >= bufInt64[EMScbData(EMS_ARR_NELEM)]) { + return false; + } tag.byte = bufTags[EMSdataTag(idx)].byte; - if (info[1]->ToBoolean()->Value()) { + if (is_full) { tag.tags.fe = EMS_TAG_FULL; } else { tag.tags.fe = EMS_TAG_EMPTY; } bufTags[EMSdataTag(idx)].byte = tag.byte; + return true; } //================================================================== // Release all the resources associated with an EMS array -void EMSdestroy(const Nan::FunctionCallbackInfo &info) { - THIS_INFO_TO_EMSBUF(info, "mmapID"); +bool EMSdestroy(int mmapID, bool do_unlink) { + void *emsBuf = emsBufs[mmapID]; if(munmap(emsBuf, emsBufLengths[mmapID]) != 0) { - Nan::ThrowError("EMSdestroy: Unable to unmap memory"); - return; + fprintf(stderr, "EMSdestroy: Unable to unmap memory\n"); + return false; } - if (info[0]->ToBoolean()->Value()) { + if (do_unlink) { if (unlink(emsBufFilenames[mmapID]) != 0) { - Nan::ThrowError("EMSdestroy: Unable to unlink file"); - return; + fprintf(stderr, "EMSdestroy: Unable to unlink file\n"); + return false; } } emsBufFilenames[mmapID][0] = 0; emsBufLengths[mmapID] = 0; emsBufs[mmapID] = NULL; + return true; } //================================================================== // Return the key of a mapped object given the EMS index -void EMSindex2key(const Nan::FunctionCallbackInfo &info) { - THIS_INFO_TO_EMSBUF(info, "mmapID"); +bool EMSindex2key(int mmapID, int64_t idx, EMSvalueType *key) { + void *emsBuf = emsBufs[mmapID]; volatile int64_t *bufInt64 = (int64_t *) emsBuf; - char *bufChar = emsBuf; + char *bufChar = (char *) emsBuf; volatile EMStag_t *bufTags = (EMStag_t *) emsBuf; volatile double *bufDouble = (double *) emsBuf; if(!EMSisMapped) { - Nan::ThrowError("EMSindex2key: Unmapping an index but Array is not mapped"); - return; + fprintf(stderr, "EMSindex2key: Unmapping an index but Array is not mapped\n"); + return false; } - int64_t idx = info[0]->ToInteger()->Value(); if (idx < 0 || idx >= bufInt64[EMScbData(EMS_ARR_NELEM)]) { - Nan::ThrowError("EMSindex2key: index out of bounds"); - return; + fprintf(stderr, "EMSindex2key: index out of bounds\n"); + return false; } - switch (bufTags[EMSmapTag(idx)].tags.type) { - case EMS_TYPE_BOOLEAN: { - bool retBool = bufInt64[EMSmapData(idx)]; - info.GetReturnValue().Set(Nan::New(retBool)); - return; - } + key->type = bufTags[EMSmapTag(idx)].tags.type; + switch (key->type) { + case EMS_TYPE_BOOLEAN: case EMS_TYPE_INTEGER: { - int32_t retInt = bufInt64[EMSmapData(idx)]; // TODO: Bug -- only 32 bits of 64? - info.GetReturnValue().Set(Nan::New(retInt)); - return; + key->value = (void *) bufInt64[EMSmapData(idx)]; + return true; } case EMS_TYPE_FLOAT: { - double retFloat = bufDouble[EMSmapData(idx)]; - info.GetReturnValue().Set(Nan::New(retFloat)); - return; - } - case EMS_TYPE_JSON: { - v8::Local retObj = Nan::New(); - retObj->Set(Nan::New("data").ToLocalChecked(), - Nan::New(EMSheapPtr(bufInt64[EMSmapData(idx)])).ToLocalChecked()); - info.GetReturnValue().Set(retObj); - return; + ulong_double alias; + alias.d = bufDouble[EMSmapData(idx)]; + key->value = (void *) alias.u64; + return true; } + case EMS_TYPE_JSON: case EMS_TYPE_STRING: { - info.GetReturnValue().Set(Nan::New(EMSheapPtr(bufInt64[EMSmapData(idx)])).ToLocalChecked()); - return; + key->value = (void *)(EMSheapPtr(bufInt64[EMSmapData(idx)])); + return true; } case EMS_TYPE_UNDEFINED: { - info.GetReturnValue().Set(Nan::Undefined()); - return; + key->value = NULL; + return true; } default: - Nan::ThrowTypeError("EMSindex2key unknown type"); - return; + fprintf(stderr, "EMSindex2key unknown type\n"); + return false; } } - - //================================================================== // Synchronize the EMS memory to persistent storage // -void EMSsync(const Nan::FunctionCallbackInfo& info) { -#if 0 - v8::HandleScope scope; - EMS_DECL(args); - int64_t *bufInt64 = (int64_t *) emsBuf; - EMStag_t *bufTags = (EMStag_t *) emsBuf; - int64_t idx; - if(args[0]->IsUndefined()) { - idx = 0; - } else { - idx = args[0]->ToInteger()->Value(); - } - - EMStag_t tag; - tag.byte = bufTags[EMSdataTag(idx)].byte; - int resultIdx = 0; - int resultTag = 0; - int resultStr = 0; -#if 1 - fprintf(stderr, "msync not complete\n"); - resultStr = 1; -#else - int flags = MS_SYNC; - char *bufChar = (char *) emsBuf; - int64_t pgsize = getpagesize(); //sysconf(_SC_PAGE_SIZE); -#define PGALIGN(addr) ((void*) (( ((uint64_t)addr) / pgsize) * pgsize )) - - //fprintf(stderr, "msync%lx %" PRIu64 " %d\n", emsBuf, length, flags); - resultIdx = msync((void*) emsBuf, pgsize, flags); - /* - if(tag.tags.type != EMS_TYPE_UNDEFINED) - //resultIdx = msync(&(bufInt64[idx]), sizeof(int64_t), flags); - resultIdx = msync( PGALIGN(&bufInt64[idx]), pgsize, flags); - if(tag.tags.type == EMS_TYPE_STRING) - //resultStr = msync(&(bufChar[bufInt64[EMScbData(EMS_ARR_HEAPBOT)] + bufInt64[idx]]), - // strlen(&(bufChar[bufInt64[EMScbData(EMS_ARR_HEAPBOT)] + bufInt64[idx]])), - // flags); - resultStr = msync( PGALIGN( &(bufChar[bufInt64[EMScbData(EMS_ARR_HEAPBOT)] + bufInt64[idx]]) ), - strlen(&(bufChar[bufInt64[EMScbData(EMS_ARR_HEAPBOT)] + bufInt64[idx]])), - flags); - //resultTag = msync(&(bufTags[EMSdataTag(idx)].byte), 1, flags); - //fprintf(stderr, "msync(%llx %" PRIu64 " %d\n", PGALIGN(&(bufTags[EMSdataTag(idx)].byte)), pgsize, flags); - // resultTag = msync(PGALIGN(&(bufTags[EMSdataTag(idx)].byte)), pgsize, flags); - // resultTag = msync(PGALIGN(&(bufTags[EMSdataTag(idx)].byte)), 1, flags); - //fprintf(stderr, "result %d %d %d\n", resultIdx, resultStr, resultTag); -*/ -#endif - if(resultIdx == 0 && resultStr == 0 && resultTag == 0) - return v8::True(); - else - return v8::False(); -#else +bool EMSsync(int mmapID) { + // resultIdx = msync((void*) emsBuf, pgsize, flags); printf("EMSsync() was called but stubbed out\n"); -#endif + return false; } //================================================================== // EMS Entry Point: Allocate and initialize the EMS domain memory // -void initialize(const Nan::FunctionCallbackInfo& info) { - if (info.Length() < 14) { - Nan::ThrowError("initialize: Missing arguments"); - return; - } - - // Parse all the arguments - int64_t nElements = info[0]->ToInteger()->Value(); - int64_t heapSize = info[1]->ToInteger()->Value(); - int64_t useMap = info[2]->ToBoolean()->Value(); - std::string filestring(*Nan::Utf8String(info[3])); - const char *filename = filestring.c_str(); - - int64_t persist = info[4]->ToBoolean()->Value(); - int64_t useExisting = info[5]->ToBoolean()->Value(); - int64_t doDataFill = info[6]->ToBoolean()->Value(); - // Data Fill type TBD during fill - int64_t fillIsJSON = info[8]->ToBoolean()->Value(); - int64_t doSetFEtags = info[9]->ToBoolean()->Value(); - int64_t setFEtags = info[10]->ToInteger()->Value(); - EMSmyID = info[11]->ToInteger()->Value(); - int64_t pinThreads = info[12]->ToBoolean()->Value(); - int64_t nThreads = info[13]->ToInteger()->Value(); - int64_t pctMLock = info[14]->ToInteger()->Value(); +int EMSinitialize(int64_t nElements, // 0 + int64_t heapSize, // 1 + bool useMap, // 2 + const char *filename, // 3 + bool persist, // 4 + bool useExisting, // 5 + bool doDataFill, // 6 + bool fillIsJSON, // 7 + EMSvalueType fillValue,// 8 + bool doSetFEtags, // 9 + bool setFEtags, // 10 + int EMSmyID, // 11 + bool pinThreads, // 12 + int64_t nThreads, // 13 + int64_t pctMLock ) { // 14 int fd; - - // Node 0 is first and always has mutual excusion during intialization + // Node 0 is first and always has mutual exclusion during initialization // perform once-only initialization here if (EMSmyID == 0) { if (!useExisting) { @@ -1001,9 +852,8 @@ void initialize(const Nan::FunctionCallbackInfo& info) { fd = shm_open(filename, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); if (fd < 0) { - perror("Error opening shared memory -- Possibly need to be root?"); - Nan::ThrowError("Unable to open file"); - return; + perror("Error opening shared memory file -- Possibly need to be root?"); + return -1; } size_t nMemBlocks = (heapSize / EMS_MEM_BLOCKSZ) + 1; @@ -1030,22 +880,22 @@ void initialize(const Nan::FunctionCallbackInfo& info) { } if (ftruncate(fd, filesize) != 0) { if (errno != EINVAL) { - fprintf(stderr, "EMS: Error during initialization, unable to set memory size to %" PRIu64 " bytes\n", + fprintf(stderr, "EMSinitialize: Error during initialization, unable to set memory size to %" PRIu64 " bytes\n", (uint64_t) filesize); - Nan::ThrowError("Unable to resize domain memory"); - return; + return -1; } } char *emsBuf = (char *) mmap(0, filesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, (off_t) 0); if (emsBuf == MAP_FAILED) { - Nan::ThrowError("Unable to map domain memory"); + fprintf(stderr, "EMSinitialize: Unable to map domain memory\n"); + return -1; } close(fd); if (nElements <= 0) pctMLock = 100; // lock RAM if master control block if (mlock((void *) emsBuf, (size_t) (filesize * (pctMLock / 100))) != 0) { - fprintf(stderr, "NOTICE: EMS thread %d was not able to lock EMS memory to RAM for %s\n", EMSmyID, filename); + fprintf(stderr, "EMSinitialize NOTICE: EMS thread %d was not able to lock EMS memory to RAM for %s\n", EMSmyID, filename); } else { // success } @@ -1093,9 +943,9 @@ void initialize(const Nan::FunctionCallbackInfo& info) { if (pinThreads) { #if defined(__linux) cpu_set_t cpuset; - CPU_ZERO(&cpuset); - CPU_SET((EMSmyID % nThreads), &cpuset); // Round-robin over-subscribed systems - sched_setaffinity(0, sizeof(cpu_set_t), &cpuset); + CPU_ZERO(&cpuset); + CPU_SET((EMSmyID % nThreads), &cpuset); // Round-robin over-subscribed systems + sched_setaffinity(0, sizeof(cpu_set_t), &cpuset); #endif } EMStag_t tag; @@ -1103,36 +953,41 @@ void initialize(const Nan::FunctionCallbackInfo& info) { int64_t iterPerThread = (nElements / nThreads) + 1; int64_t startIter = iterPerThread * EMSmyID; int64_t endIter = iterPerThread * (EMSmyID + 1); + size_t fillStrLen = 0; + if (doDataFill && (fillValue.type == EMS_TYPE_JSON || fillValue.type == EMS_TYPE_STRING)) { + fillStrLen = strlen((char *)fillValue.value); + } if (endIter > nElements) endIter = nElements; for (int64_t idx = startIter; idx < endIter; idx++) { tag.tags.rw = 0; if (doDataFill) { - tag.tags.type = EMSv8toEMStype(info[7], fillIsJSON); + tag.tags.type = fillValue.type; switch (tag.tags.type) { + case EMS_TYPE_BOOLEAN: case EMS_TYPE_INTEGER: - bufInt64[EMSdataData(idx)] = info[7]->ToInteger()->Value(); + bufInt64[EMSdataData(idx)] = (int64_t) fillValue.value; break; - case EMS_TYPE_FLOAT: - bufDouble[EMSdataData(idx)] = info[7]->ToNumber()->Value(); + case EMS_TYPE_FLOAT: { + ulong_double alias; + alias.u64 = (uint64_t) fillValue.value; + bufDouble[EMSdataData(idx)] = alias.d; + } break; case EMS_TYPE_UNDEFINED: bufInt64[EMSdataData(idx)] = 0xdeadbeef; break; - case EMS_TYPE_BOOLEAN: - bufInt64[EMSdataData(idx)] = info[7]->ToBoolean()->Value(); - break; case EMS_TYPE_JSON: case EMS_TYPE_STRING: { - std::string fill_str(*Nan::Utf8String(info[7])); int64_t textOffset; - EMS_ALLOC(textOffset, fill_str.length() + 1, "EMSinit: out of memory to store string", ); + EMS_ALLOC(textOffset, fillStrLen + 1, bufChar, + "EMSinitialize: out of memory to store string", false); bufInt64[EMSdataData(idx)] = textOffset; - strcpy(EMSheapPtr(textOffset), fill_str.c_str()); + strcpy(EMSheapPtr(textOffset), (const char *) fillValue.value); } break; default: - Nan::ThrowError("EMSinit: type is unknown"); - return; + fprintf(stderr, "EMSinitialize: fill type is unknown\n"); + return -1; } } else { tag.tags.type = EMS_TYPE_UNDEFINED; @@ -1153,8 +1008,6 @@ void initialize(const Nan::FunctionCallbackInfo& info) { } } - // ======================================================================================== - v8::Local obj = Nan::New(); int emsBufN = 0; while(emsBufN < EMS_MAX_N_BUFS && emsBufs[emsBufN] != NULL) emsBufN++; if(emsBufN < EMS_MAX_N_BUFS) { @@ -1162,57 +1015,10 @@ void initialize(const Nan::FunctionCallbackInfo& info) { emsBufLengths[emsBufN] = filesize; strncpy(emsBufFilenames[emsBufN], filename, MAX_FNAME_LEN); } else { - fprintf(stderr, "ERROR: Unable to allocate a buffer ID/index\n"); + fprintf(stderr, "EMSinitialize: ERROR - Unable to allocate a buffer ID/index\n"); + emsBufN = -1; } - obj->Set(Nan::New("mmapID").ToLocalChecked(), Nan::New(emsBufN)); - -#define ADD_FUNC_TO_V8_OBJ(obj, func_name, func) \ - { \ - v8::Local tpl = Nan::New(func); \ - v8::Local fn = tpl->GetFunction(); \ - fn->SetName(Nan::New(func_name).ToLocalChecked()); \ - obj->Set(Nan::New(func_name).ToLocalChecked(), tpl->GetFunction()); \ - } - - ADD_FUNC_TO_V8_OBJ(obj, "initialize", initialize); - ADD_FUNC_TO_V8_OBJ(obj, "faa", EMSfaa); - ADD_FUNC_TO_V8_OBJ(obj, "cas", EMScas); - ADD_FUNC_TO_V8_OBJ(obj, "read", EMSread); - ADD_FUNC_TO_V8_OBJ(obj, "write", EMSwrite); - ADD_FUNC_TO_V8_OBJ(obj, "readRW", EMSreadRW); - ADD_FUNC_TO_V8_OBJ(obj, "releaseRW", EMSreleaseRW); - ADD_FUNC_TO_V8_OBJ(obj, "readFE", EMSreadFE); - ADD_FUNC_TO_V8_OBJ(obj, "readFF", EMSreadFF); - ADD_FUNC_TO_V8_OBJ(obj, "setTag", EMSsetTag); - ADD_FUNC_TO_V8_OBJ(obj, "writeEF", EMSwriteEF); - ADD_FUNC_TO_V8_OBJ(obj, "writeXF", EMSwriteXF); - ADD_FUNC_TO_V8_OBJ(obj, "writeXE", EMSwriteXE); - ADD_FUNC_TO_V8_OBJ(obj, "push", EMSpush); - ADD_FUNC_TO_V8_OBJ(obj, "pop", EMSpop); - ADD_FUNC_TO_V8_OBJ(obj, "enqueue", EMSenqueue); - ADD_FUNC_TO_V8_OBJ(obj, "dequeue", EMSdequeue); - ADD_FUNC_TO_V8_OBJ(obj, "sync", EMSsync); - ADD_FUNC_TO_V8_OBJ(obj, "index2key", EMSindex2key); - ADD_FUNC_TO_V8_OBJ(obj, "destroy", EMSdestroy); - info.GetReturnValue().Set(obj); - - return; -} - - -//--------------------------------------------------------------- -static void RegisterModule(v8::Handle target) { - ADD_FUNC_TO_V8_OBJ(target, "initialize", initialize); - ADD_FUNC_TO_V8_OBJ(target, "barrier", EMSbarrier); - ADD_FUNC_TO_V8_OBJ(target, "singleTask", EMSsingleTask); - ADD_FUNC_TO_V8_OBJ(target, "criticalEnter", EMScriticalEnter); - ADD_FUNC_TO_V8_OBJ(target, "criticalExit", EMScriticalExit); - ADD_FUNC_TO_V8_OBJ(target, "loopInit", EMSloopInit); - ADD_FUNC_TO_V8_OBJ(target, "loopChunk", EMSloopChunk); - // ADD_FUNC_TO_V8_OBJ(target, "sync", EMSsync); - // ADD_FUNC_TO_V8_OBJ(target, "length"); - // ADD_FUNC_TO_V8_OBJ(target, "Buffer"); + return emsBufN; } -NODE_MODULE(ems, RegisterModule); diff --git a/ems.h b/ems.h index cd625a0..17157ba 100644 --- a/ems.h +++ b/ems.h @@ -1,5 +1,5 @@ /*-----------------------------------------------------------------------------+ - | Extended Memory Semantics (EMS) Version 1.2.0 | + | Extended Memory Semantics (EMS) Version 1.3.0 | | Synthetic Semantics http://www.synsem.com/ mogill@synsem.com | +-----------------------------------------------------------------------------+ | Copyright (c) 2011-2014, Synthetic Semantics LLC. All rights reserved. | @@ -31,11 +31,11 @@ +-----------------------------------------------------------------------------*/ #ifndef EMSPROJ_EMS_H #define EMSPROJ_EMS_H -#include -#include #define __STDC_FORMAT_MACROS 1 +#include +#include +#include #include -#include "nan.h" #include #include #include @@ -48,10 +48,10 @@ #if !defined _GNU_SOURCE # define _GNU_SOURCE #endif +#include #include "ems_alloc.h" - //================================================================== // EMS Full/Empty Tag States // @@ -65,7 +65,7 @@ //================================================================== // EMS Data types // -#define EMS_TYPE_UNALLOCATED ((unsigned char)0) +#define EMS_TYPE_INVALID ((unsigned char)0) #define EMS_TYPE_BOOLEAN ((unsigned char)1) #define EMS_TYPE_STRING ((unsigned char)2) #define EMS_TYPE_FLOAT ((unsigned char)3) @@ -99,7 +99,6 @@ //================================================================== // EMS Control Block -- Global State for EMS -// #define EMS_CB_NTHREADS 0 // Number of threads #define EMS_CB_NBAR0 1 // Number of threads at Barrier 0 #define EMS_CB_NBAR1 2 // Number of threads at Barrier 1 @@ -121,11 +120,9 @@ //================================================================== // Pointers to mmapped EMS buffers -// #define EMS_MAX_N_BUFS 4096 #define MAX_NUMBER2STR_LEN 40 // Maximum number of characters in a %d or %f format #define MAX_FNAME_LEN 256 -#define MAX_KEY_LEN 256 extern char *emsBufs[EMS_MAX_N_BUFS]; extern size_t emsBufLengths[EMS_MAX_N_BUFS]; extern char emsBufFilenames[EMS_MAX_N_BUFS][MAX_FNAME_LEN]; @@ -137,7 +134,6 @@ extern char emsBufFilenames[EMS_MAX_N_BUFS][MAX_FNAME_LEN]; //================================================================== // Macros to translate from EMS Data Indexes and EMS Control Block // indexes to offsets in the EMS shared memory -// #define EMSwordSize (sizeof(size_t)) #define EMSnWordsPerTagWord (EMSwordSize-1) #define EMSnWordsPerLine EMSwordSize @@ -152,7 +148,6 @@ extern char emsBufFilenames[EMS_MAX_N_BUFS][MAX_FNAME_LEN]; // Untagged Memory // Malloc: Storage for the free/used structures // Heap: Open Heap storage -// #define EMSappIdx2emsIdx(idx) ((((idx) / EMSnWordsPerTagWord) * EMSnWordsPerLine) + ((idx) % EMSnWordsPerTagWord) ) #define EMSappIdx2LineIdx(idx) ( ((idx) / EMSnWordsPerTagWord) * EMSnWordsPerLine) #define EMSappIdx2TagWordIdx(idx) ( EMSappIdx2LineIdx(idx) + EMSnWordsPerTagWord ) @@ -167,10 +162,11 @@ extern char emsBufFilenames[EMS_MAX_N_BUFS][MAX_FNAME_LEN]; #define EMSmapTag(idx) ( EMSappTag2emsTag((idx) + EMS_ARR_CB_SIZE + bufInt64[EMScbData(EMS_ARR_NELEM)]) ) #define EMSheapPtr(idx) ( &bufChar[ bufInt64[EMScbData(EMS_ARR_HEAPBOT)] + (idx) ] ) +#define EMS_MEM_MALLOCBOT(bufChar) ((struct emsMem *) &bufChar[ bufInt64[EMScbData(EMS_ARR_MALLOCBOT)] ]) + //================================================================== // The EMS Tag structure -// #define EMS_TYPE_NBITS_FE 2U #define EMS_TYPE_NBITS_TYPE 3U #define EMS_TYPE_NBITS_RW 3U @@ -185,24 +181,11 @@ union EMStag_t { unsigned char byte; }; -//================================================================== -// Determine the EMS type of a V8 argument -#define EMSv8toEMStype(arg, stringIsJSON) \ -( \ - arg->IsInt32() ? EMS_TYPE_INTEGER : \ - arg->IsNumber() ? EMS_TYPE_FLOAT : \ - (arg->IsString() && !stringIsJSON) ? EMS_TYPE_STRING : \ - (arg->IsString() && stringIsJSON) ? EMS_TYPE_JSON : \ - arg->IsBoolean() ? EMS_TYPE_BOOLEAN : \ - arg->IsUndefined() ? EMS_TYPE_UNDEFINED: \ - arg->IsUint32() ? EMS_TYPE_INTEGER : -1 \ -) //================================================================== // Yield the processor and sleep (using exponential decay) without // using resources/ // Used within spin-loops to reduce hot-spotting -// #define RESET_NAP_TIME int EMScurrentNapTime; #define MAX_NAP_TIME 1000000 #define NANOSLEEP { \ @@ -220,47 +203,76 @@ union EMStag_t { // Type-punning is now a warning in GCC, but this syntax is still okay union ulong_double { double d; - unsigned long u; + uint64_t u64; }; -//================================================================== -// Macro to declare and unwrap the EMS buffer, used to access the -// EMSarray object metadata -// -#define THIS_INFO_TO_EMSBUF(info, prop_name) \ - int mmapID = JS_PROP_TO_VALUE(isolate, info.This(), prop_name)->ToInteger()->Value(); \ - char *emsBuf = emsBufs[mmapID]; -#define JS_ARG_TO_OBJ(arg) v8::Handle::Cast(arg) -#define JS_PROP_TO_VALUE(isolate, obj, property) JS_ARG_TO_OBJ(obj)->Get(Nan::New(property).ToLocalChecked()) - -void EMScriticalEnter(const Nan::FunctionCallbackInfo& info); -void EMScriticalExit(const Nan::FunctionCallbackInfo& info); -void EMSbarrier(const Nan::FunctionCallbackInfo& info); -void EMSsingleTask(const Nan::FunctionCallbackInfo& info); -void EMScas(const Nan::FunctionCallbackInfo &info); -void EMSfaa(const Nan::FunctionCallbackInfo& info); -void EMSpush(const Nan::FunctionCallbackInfo &info); -void EMSpop(const Nan::FunctionCallbackInfo &info); -void EMSenqueue(const Nan::FunctionCallbackInfo &info); -void EMSdequeue(const Nan::FunctionCallbackInfo &info); -void EMSloopInit(const Nan::FunctionCallbackInfo& info); -void EMSloopChunk(const Nan::FunctionCallbackInfo& info); -unsigned char EMStransitionFEtag(EMStag_t volatile *tag, EMStag_t volatile *mapTag, unsigned char oldFE, unsigned char newFE, unsigned char oldType); -int64_t EMSwriteIndexMap(const Nan::FunctionCallbackInfo& info); -int64_t EMSreadIndexMap(const Nan::FunctionCallbackInfo& info); -void EMSindex2key(const Nan::FunctionCallbackInfo &info); -// int64_t EMShashString(const char *key); -#define EMS_ALLOC(addr, len, errmsg, retval) \ - addr = emsMutexMem_alloc( (struct emsMem *) &bufChar[ bufInt64[EMScbData(EMS_ARR_MALLOCBOT)] ], \ +typedef struct { + unsigned char type; + void *value; +} EMSvalueType; + + +int EMScriticalEnter(int mmapID, int timeout); +bool EMScriticalExit(int mmapID); +int EMSbarrier(int mmapID, int timeout); +bool EMSsingleTask(int mmapID); +bool EMScas(int mmapID, EMSvalueType *key, + EMSvalueType *oldValue, EMSvalueType *newValue, + EMSvalueType *returnValue); +bool EMSfaa(int mmapID, EMSvalueType *key, EMSvalueType *value, EMSvalueType *returnValue); +int EMSpush(int mmapID, EMSvalueType *value); +bool EMSpop(int mmapID, EMSvalueType *returnValue); +int EMSenqueue(int mmapID, EMSvalueType *value); +bool EMSdequeue(int mmapID, EMSvalueType *returnValue); +bool EMSloopInit(int mmapID, int32_t start, int32_t end, int32_t minChunk, int schedule_mode); +bool EMSloopChunk(int mmapID, int32_t *start, int32_t *end); +unsigned char EMStransitionFEtag(EMStag_t volatile *tag, EMStag_t volatile *mapTag, unsigned char oldFE, unsigned char newFE, unsigned char oldType); +int64_t EMSwriteIndexMap(const int mmapID, EMSvalueType *key); +int64_t EMSkey2index(void *emsBuf, EMSvalueType *key, bool is_mapped); +int64_t EMShashString(const char *key); + +///////////////////////////////////////////////////////////////////////// +bool EMSreadRW(const int mmapID, EMSvalueType *key, EMSvalueType *returnValue); +bool EMSreadFF(const int mmapID, EMSvalueType *key, EMSvalueType *returnValue); +bool EMSreadFE(const int mmapID, EMSvalueType *key, EMSvalueType *returnValue); +bool EMSread(const int mmapID, EMSvalueType *key, EMSvalueType *returnValue); +int EMSreleaseRW(const int mmapID, EMSvalueType *key); +bool EMSwriteXF(int mmapID, EMSvalueType *key, EMSvalueType *value); +bool EMSwriteXE(int mmapID, EMSvalueType *key, EMSvalueType *value); +bool EMSwriteEF(int mmapID, EMSvalueType *key, EMSvalueType *value); +bool EMSwrite(int mmapID, EMSvalueType *key, EMSvalueType *value); +bool EMSsetTag(int mmapID, EMSvalueType *key, bool is_full); +bool EMSdestroy(int mmapID, bool do_unlink); +bool EMSindex2key(int mmapID, int64_t idx, EMSvalueType *key); +bool EMSsync(int mmapID); +int EMSinitialize(int64_t nElements, // 0 + int64_t heapSize, // 1 + bool useMap, // 2 + const char *filename, // 3 + bool persist, // 4 + bool useExisting, // 5 + bool doDataFill, // 6 + bool fillIsJSON, // 7 + EMSvalueType fillValue,// 8 + bool doSetFEtags, // 9 + bool setFEtags, // 10 + int EMSmyID, // 11 + bool pinThreads, // 12 + int64_t nThreads, // 13 + int64_t pctMLock ); // 14 + + +#define EMS_ALLOC(addr, len, bufChar, errmsg, retval) \ + addr = emsMutexMem_alloc( EMS_MEM_MALLOCBOT(bufChar), \ len, (char*) &bufInt64[EMScbData(EMS_ARR_MEM_MUTEX)] ); \ if(addr < 0) { \ - Nan::ThrowError(errmsg); \ + fprintf(stderr, "EMS_ALLOC: ERROR -- Allocation failed\n"); \ return retval; \ } #define EMS_FREE(addr) \ - emsMutexMem_free( (struct emsMem *) &bufChar[ bufInt64[EMScbData(EMS_ARR_MALLOCBOT)] ], \ + emsMutexMem_free( EMS_MEM_MALLOCBOT(bufChar), \ addr, (char*) &bufInt64[EMScbData(EMS_ARR_MEM_MUTEX)] ) size_t emsMutexMem_alloc(struct emsMem *heap, // Base of EMS malloc structs @@ -272,7 +284,6 @@ void emsMutexMem_free(struct emsMem *heap, // Base of EMS malloc structs extern int EMSmyID; // EMS Thread ID - #define EMSisMapped (bufInt64[EMScbData(EMS_ARR_MAPBOT)]*(int64_t)EMSwordSize != bufInt64[EMScbData(EMS_ARR_MALLOCBOT)]) #endif //EMSPROJ_EMS_H diff --git a/ems_alloc.cc b/ems_alloc.cc index 321ac5b..bdd372d 100644 --- a/ems_alloc.cc +++ b/ems_alloc.cc @@ -1,5 +1,5 @@ /*-----------------------------------------------------------------------------+ - | Extended Memory Semantics (EMS) Version 1.0.0 | + | Extended Memory Semantics (EMS) Version 1.3.0 | | Synthetic Semantics http://www.synsem.com/ mogill@synsem.com | +-----------------------------------------------------------------------------+ | Copyright (c) 2011-2014, Synthetic Semantics LLC. All rights reserved. | @@ -31,8 +31,6 @@ +-----------------------------------------------------------------------------*/ #include "ems_alloc.h" #include -#include -#include #include #include @@ -63,7 +61,7 @@ void emsMem_delete(struct emsMem *self) { //-----------------------------------------------------------------------------+ // Pow-2 utility functions for 64 bit -uint64_t emsNextPow2(uint64_t x) { +size_t emsNextPow2(uint64_t x) { if (__builtin_popcountl(x) == 1) return x; // Yes, that will overflow on 63 bit numbers. Hopefully someone will rewrite this by then. // fprintf(stderr, ">>>lz=%d shift=%d\n", __builtin_clzl(x), (64 - __builtin_clzl(x))); @@ -72,7 +70,7 @@ uint64_t emsNextPow2(uint64_t x) { static inline int64_t EMS_index_offset(int64_t index, int32_t level, int64_t max_level) { - return ((index + 1) - (1UL << level)) << (max_level - level); + return ((index + 1) - (1L << level)) << (max_level - level); } @@ -93,11 +91,11 @@ static void EMS_mark_parent(struct emsMem *self, int64_t index) { //-----------------------------------------------------------------------------+ // Allocate new memory from the EMS heap -int64_t emsMem_alloc(struct emsMem *self, int64_t s) { - int64_t size; - size = emsNextPow2(((s + (EMS_MEM_BLOCKSZ - 1)) / EMS_MEM_BLOCKSZ)); +int64_t emsMem_alloc(struct emsMem *self, int64_t bytesRequested) { + size_t size; + size = emsNextPow2((bytesRequested + (EMS_MEM_BLOCKSZ - 1)) / EMS_MEM_BLOCKSZ); if (size == 0) size++; - int64_t length = 1UL << self->level; + size_t length = 1L << self->level; // fprintf(stderr, "emsMem_alloc: self=%x size=%ld s=%ld len=%ld\n", self, size, s, length); if (size > length) return -1; @@ -212,10 +210,10 @@ int64_t emsMem_size(struct emsMem *self, int64_t offset) { switch (self->tree[index]) { case BUDDY_USED: assert(offset == left); - return length; + return length * EMS_MEM_BLOCKSZ; case BUDDY_UNUSED: assert(0); - return length; + return length * EMS_MEM_BLOCKSZ; default: length /= 2; if (offset < left + length) { diff --git a/ems_alloc.h b/ems_alloc.h index 8d2e830..23153ed 100644 --- a/ems_alloc.h +++ b/ems_alloc.h @@ -1,5 +1,5 @@ /*-----------------------------------------------------------------------------+ - | Extended Memory Semantics (EMS) Version 1.0.0 | + | Extended Memory Semantics (EMS) Version 1.3.0 | | Synthetic Semantics http://www.synsem.com/ mogill@synsem.com | +-----------------------------------------------------------------------------+ | Copyright (c) 2011-2014, Synthetic Semantics LLC. All rights reserved. | @@ -48,10 +48,10 @@ struct emsMem { struct emsMem *emsMem_new(int level); void emsMem_delete(struct emsMem *); -int64_t emsMem_alloc(struct emsMem *, int64_t size); +int64_t emsMem_alloc(struct emsMem *, int64_t bytesRequested); void emsMem_free(struct emsMem *, int64_t offset); int64_t emsMem_size(struct emsMem *, int64_t offset); void emsMem_dump(struct emsMem *); -uint64_t emsNextPow2( uint64_t x ); +size_t emsNextPow2(uint64_t x); #endif diff --git a/index.js b/index.js index 5b86483..982651d 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,5 @@ /*-----------------------------------------------------------------------------+ - | Extended Memory Semantics (EMS) Version 1.2.0 | + | Extended Memory Semantics (EMS) Version 1.3.0 | | Synthetic Semantics http://www.synsem.com/ mogill@synsem.com | +-----------------------------------------------------------------------------+ | Copyright (c) 2011-2014, Synthetic Semantics LLC. All rights reserved. | @@ -37,10 +37,8 @@ var EMSglobal; //================================================================== // Convert the different possible index types into a linear EMS index -// function EMSidx(indexes, // Index given by application - EMSarray // EMS array being indexed -) { + EMSarray) { // EMS array being indexed var idx = 0; if (indexes instanceof Array) { // Is a Multidimension array: [x,y,z] indexes.forEach(function (x, i) { @@ -62,7 +60,6 @@ function EMSidx(indexes, // Index given by application //================================================================== // Print a message to the console with a prefix indicating which // thread is printing -// function EMSdiag(text) { console.log("EMStask " + this.myID + ": " + text); } @@ -70,7 +67,6 @@ function EMSdiag(text) { //================================================================== // Co-Begin a parallel region, executing the function 'func' -// function EMSparallel() { EMSglobal.inParallelContext = true; var user_args = (arguments.length === 1?[arguments[0]]:Array.apply(null, arguments)); @@ -89,13 +85,11 @@ function EMSparallel() { //================================================================== // Execute the local iterations of a decomposed loop with // the specified scheduling. -// function EMSparForEach(start, // First iteration's index end, // Final iteration's index loopBody, // Function to execute each iteration scheduleType, // Load balancing technique - minChunk // Smallest block of iterations -) { + minChunk) { // Smallest block of iterations var sched = scheduleType; var idx; if (typeof sched === 'undefined') { @@ -113,7 +107,7 @@ function EMSparForEach(start, // First iteration's index if (e > end) { e = end; } - for (idx = s; idx < e; idx++) { + for (idx = s; idx < e; idx += 1) { loopBody(idx); } break; @@ -146,7 +140,6 @@ function EMSparForEach(start, // First iteration's index // Input is an array containing EMS elements to transition from // Full to Empty: // arr = [ [ emsArr0, idx0 ], [ emsArr1, idx1, true ], [ emsArr2, idx2 ] ] -// function EMStmStart(emsElems) { var MAX_ELEM_PER_REGION = 10000000000000; @@ -182,24 +175,16 @@ function EMStmStart(emsElems) { // Commit or abort a transaction // The tmHandle contains the result from tmStart: // [ [ EMSarray, index, isReadOnly, origValue ], ... ] -// -function EMStmEnd(tmHandle, // The returned value from tmStart - doCommit // Commit or Abort the transaction -) { +function EMStmEnd(tmHandle, // The returned value from tmStart + doCommit) { // Commit or Abort the transaction tmHandle.forEach(function (emsElem) { - if (doCommit) { - if (emsElem[2] === true) { - // Is a read-only element - emsElem[0].releaseRW(emsElem[1]); - } else { - // Is read-write, keep current value and mark Full - emsElem[0].setTag(emsElem[1], 'full'); - } + if (emsElem[2] === true) { + // Is a read-only element + emsElem[0].releaseRW(emsElem[1]); } else { - // Abort the transaction - if (emsElem[2] === true) { - // Is read-only element - emsElem[0].releaseRW(emsElem[1]); + if (doCommit) { + // Is read-write, keep current value and mark Full + emsElem[0].setTag(emsElem[1], true); } else { // Write back the original value and mark full emsElem[0].writeEF(emsElem[1], emsElem[3]); @@ -229,8 +214,12 @@ function EMSsync(emsArr) { //================================================================== // Synchronize memory with storage -// function EMSindex2key(index) { + if(typeof(index) != 'number') { + console.log('EMSindex2key: Index (' + index + ') is not an integer'); + return undefined; + } + return this.data.index2key(index); } @@ -268,29 +257,29 @@ function EMSenqueue(value) { // into EMS linear addresses // Apparently it is illegal to pass a native function as an argument function EMSwrite(indexes, value) { - var nativeIndex = EMSidx(indexes, this); + var linearIndex = EMSidx(indexes, this); if (typeof value == 'object') { - this.data.write(nativeIndex, JSON.stringify(value), true); + this.data.write(linearIndex, JSON.stringify(value), true); } else { - this.data.write(nativeIndex, value); + this.data.write(linearIndex, value); } } function EMSwriteEF(indexes, value) { - var nativeIndex = EMSidx(indexes, this); + var linearIndex = EMSidx(indexes, this); if (typeof value == 'object') { - this.data.writeEF(nativeIndex, JSON.stringify(value), true); + this.data.writeEF(linearIndex, JSON.stringify(value), true); } else { - this.data.writeEF(nativeIndex, value); + this.data.writeEF(linearIndex, value); } } function EMSwriteXF(indexes, value) { - var nativeIndex = EMSidx(indexes, this); + var linearIndex = EMSidx(indexes, this); if (typeof value == 'object') { - this.data.writeXF(nativeIndex, JSON.stringify(value), true); + this.data.writeXF(linearIndex, JSON.stringify(value), true); } else { - this.data.writeXF(nativeIndex, value); + this.data.writeXF(linearIndex, value); } } @@ -320,32 +309,28 @@ function EMSreadRW(indexes) { } function EMSreleaseRW(indexes) { - return EMSreturnData(this.data.releaseRW(EMSidx(indexes, this))) + return this.data.releaseRW(EMSidx(indexes, this)) } function EMSsetTag(indexes, fe) { - return this.data.setTag(EMSidx(indexes, this), (fe == 'full')) + return this.data.setTag(EMSidx(indexes, this), fe) } function EMSfaa(indexes, val) { if (typeof val == 'object') { console.log("EMSfaa: Cannot add an object to something"); - return (val); + return undefined; } else { - return this.data.faa(EMSidx(indexes, this), val); // Can only return JSON primitives + return this.data.faa(EMSidx(indexes, this), val); // No returnData(), FAA can only return JSON primitives } } function EMScas(indexes, oldVal, newVal) { - if (typeof oldVal == 'object') { - console.log("EMScas: Cannot compare objects, only JSON primitives"); - return (undefined); + if (typeof newVal == 'object') { + console.log("EMScas: ERROR -- objects are not a valid new type"); + return undefined; } else { - if (typeof newVal == 'object') { - return this.data.cas(EMSidx(indexes, this), oldVal, JSON.stringify(newVal), true); - } else { - return this.data.cas(EMSidx(indexes, this), oldVal, newVal); - } + return this.data.cas(EMSidx(indexes, this), oldVal, newVal); } } @@ -377,7 +362,6 @@ function EMSmaster(func) { // runtime cannot tell apart. Barriers are phased, so a barrier // is used to prevent any thread from entering the next single- // execution region before this one is complete -// function EMSsingle(func) { var retObj; if (this.singleTask()) { @@ -399,7 +383,6 @@ function EMSbarrier() { //================================================================== // Utility functions for determining types -// function EMSisArray(a) { return ( (typeof a.pop !== 'undefined') ) } @@ -415,7 +398,6 @@ function EMSisDefined(x) { //================================================================== // Release all resources associated with an EMS memory region -// function EMSdestroy(unlink_file) { EMSbarrier(); if (EMSglobal.myID == 0) { @@ -426,7 +408,6 @@ function EMSdestroy(unlink_file) { //================================================================== // Creating a new EMS memory region -// function EMSnew(arg0, // Maximum number of elements the EMS region can hold heapSize, // #bytes of memory reserved for strings/arrays/objs/maps/etc filename // Optional filename for persistent EMS memory @@ -529,19 +510,21 @@ function EMSnew(arg0, // Maximum number of elements the EMS region can h return; } } + // init() is first called from thread 0 to perform one-thread // only operations (ie: unlinking an old file, opening a new // file). After thread 0 has completed initialization, other // threads can safely share the EMS array. if (!emsDescriptor.useExisting && this.myID != 0) EMSbarrier(); - emsDescriptor.data = this.init(emsDescriptor.nElements, emsDescriptor.heapSize, - emsDescriptor.useMap, emsDescriptor.filename, - emsDescriptor.persist, emsDescriptor.useExisting, - emsDescriptor.doDataFill, emsDescriptor.dataFill, fillIsJSON, - (typeof emsDescriptor.setFEtags !== 'undefined'), - (emsDescriptor.setFEtags == 'full'), - this.myID, this.pinThreads, this.nThreads, - emsDescriptor.mlock); + emsDescriptor.data = this.init(emsDescriptor.nElements, emsDescriptor.heapSize, // 0, 1 + emsDescriptor.useMap, emsDescriptor.filename, // 2, 3 + emsDescriptor.persist, emsDescriptor.useExisting, // 4, 5 + emsDescriptor.doDataFill, fillIsJSON, // 6, 7 + emsDescriptor.dataFill, // 8 + (typeof emsDescriptor.setFEtags !== 'undefined'), // 9 + (emsDescriptor.setFEtags == 'full'), // 10 + this.myID, this.pinThreads, this.nThreads, // 11, 12, 13 + emsDescriptor.mlock); // 14 if (!emsDescriptor.useExisting && this.myID == 0) EMSbarrier(); emsDescriptor.regionN = this.newRegionN; @@ -598,8 +581,12 @@ function ems_wrapper(nThreadsArg, pinThreadsArg, threadingType, filename) { var domainName = '/EMS_MainDomain'; if (filename) domainName = filename; // All arguments are defined -- now do the EMS initialization - retObj.data = EMS.initialize(0, 0, false, domainName, false, false, - false, 0, false, false, 0, retObj.myID, pinThreads, nThreads); + retObj.data = EMS.initialize(0, 0, // 0= # elements, 1=Heap Size + false, // 2 = useMap + domainName, false, false, // 3=name, 4=persist, 5=useExisting + false, false, undefined, // 6=doDataFill, 7=fillIsJSON, 8=fillValue + false, false, retObj.myID, // 9=doSetFEtags, 10=setFEtags, 11=EMS myID + pinThreads, nThreads, 99); // 12=pinThread, 13=nThreads, 14=pctMlock var targetScript; switch (threadingType) { diff --git a/loops.cc b/loops.cc index 20222c3..fed86a5 100644 --- a/loops.cc +++ b/loops.cc @@ -1,5 +1,5 @@ /*-----------------------------------------------------------------------------+ - | Extended Memory Semantics (EMS) Version 1.0.0 | + | Extended Memory Semantics (EMS) Version 1.3.0 | | Synthetic Semantics http://www.synsem.com/ mogill@synsem.com | +-----------------------------------------------------------------------------+ | Copyright (c) 2011-2014, Synthetic Semantics LLC. All rights reserved. | @@ -35,44 +35,31 @@ //================================================================== // Parallel Loop -- context initialization // -void EMSloopInit(const Nan::FunctionCallbackInfo& info) { - THIS_INFO_TO_EMSBUF(info, "mmapID"); +bool EMSloopInit(int mmapID, int32_t start, int32_t end, int32_t minChunk, int schedule_mode) { + void *emsBuf = emsBufs[mmapID]; int32_t *bufInt32 = (int32_t *) emsBuf; - - int start = 0; - int end = 0; - int minChunk = 0; - - if (info.Length() != 4) { - Nan::ThrowError("EMSloopInit: Wrong number of args"); - return; - } - - start = info[0]->ToInteger()->Value(); - end = info[1]->ToInteger()->Value(); - std::string sched_string(*Nan::Utf8String(info[2])); - const char *schedule = sched_string.c_str(); - minChunk = info[3]->ToInteger()->Value(); + bool success = true; bufInt32[EMS_LOOP_IDX] = start; bufInt32[EMS_LOOP_START] = start; bufInt32[EMS_LOOP_END] = end; - if (strcmp(schedule, "guided") == 0) { - bufInt32[EMS_LOOP_CHUNKSZ] = ((end - start) / 2) / bufInt32[EMS_CB_NTHREADS]; - if (bufInt32[EMS_LOOP_CHUNKSZ] < minChunk) bufInt32[EMS_LOOP_CHUNKSZ] = minChunk; - bufInt32[EMS_LOOP_MINCHUNK] = minChunk; - bufInt32[EMS_LOOP_SCHED] = EMS_SCHED_GUIDED; - } else { - if (strcmp(schedule, "dynamic") == 0) { + switch (schedule_mode) { + case EMS_SCHED_GUIDED: + bufInt32[EMS_LOOP_CHUNKSZ] = ((end - start) / 2) / bufInt32[EMS_CB_NTHREADS]; + if (bufInt32[EMS_LOOP_CHUNKSZ] < minChunk) bufInt32[EMS_LOOP_CHUNKSZ] = minChunk; + bufInt32[EMS_LOOP_MINCHUNK] = minChunk; + bufInt32[EMS_LOOP_SCHED] = EMS_SCHED_GUIDED; + break; + case EMS_SCHED_DYNAMIC: bufInt32[EMS_LOOP_CHUNKSZ] = 1; bufInt32[EMS_LOOP_MINCHUNK] = 1; bufInt32[EMS_LOOP_SCHED] = EMS_SCHED_DYNAMIC; - } else { - Nan::ThrowError("EMSloopInit: Unknown schedule type"); - return; - } + break; + default: + fprintf(stderr, "NodeJSloopInit: Unknown schedule modes\n"); + success = false; } - info.GetReturnValue().Set(Nan::New(EMS_LOOP_IDX)); + return success; } @@ -81,27 +68,23 @@ void EMSloopInit(const Nan::FunctionCallbackInfo& info) { // an idle thread // JQM TODO BUG -- convert to 64 bit using fe tags // -void EMSloopChunk(const Nan::FunctionCallbackInfo& info) { - THIS_INFO_TO_EMSBUF(info, "mmapID"); +bool EMSloopChunk(int mmapID, int32_t *start, int32_t *end) { + void *emsBuf = emsBufs[mmapID]; int32_t *bufInt32 = (int32_t *) emsBuf; int chunkSize = bufInt32[EMS_LOOP_CHUNKSZ]; - int start = __sync_fetch_and_add(&(bufInt32[EMS_LOOP_IDX]), chunkSize); - int end = start + chunkSize; - - if (start > bufInt32[EMS_LOOP_END]) end = 0; - if (end > bufInt32[EMS_LOOP_END]) end = bufInt32[EMS_LOOP_END]; - - v8::Local retObj = Nan::New(); - retObj->Set(Nan::New("start").ToLocalChecked(), Nan::New(start)); - retObj->Set(Nan::New("end").ToLocalChecked(), Nan::New(end)); + *start = __sync_fetch_and_add(&(bufInt32[EMS_LOOP_IDX]), chunkSize); + *end = *start + chunkSize; + if (*start > bufInt32[EMS_LOOP_END]) *end = 0; + if (*end > bufInt32[EMS_LOOP_END]) *end = bufInt32[EMS_LOOP_END]; if (bufInt32[EMS_LOOP_SCHED] == EMS_SCHED_GUIDED) { // Compute the size of the chunk the next thread should use - int newSz = ((bufInt32[EMS_LOOP_END] - start) / 2) / bufInt32[EMS_CB_NTHREADS]; + int newSz = (int) ((bufInt32[EMS_LOOP_END] - *start) / 2) / bufInt32[EMS_CB_NTHREADS]; if (newSz < bufInt32[EMS_LOOP_MINCHUNK]) newSz = bufInt32[EMS_LOOP_MINCHUNK]; bufInt32[EMS_LOOP_CHUNKSZ] = newSz; } - info.GetReturnValue().Set(retObj); + + return true; } diff --git a/nodejs.cc b/nodejs.cc new file mode 100644 index 0000000..be1eaff --- /dev/null +++ b/nodejs.cc @@ -0,0 +1,377 @@ +/*-----------------------------------------------------------------------------+ + | Extended Memory Semantics (EMS) Version 1.3.0 | + | Synthetic Semantics http://www.synsem.com/ mogill@synsem.com | + +-----------------------------------------------------------------------------+ + | Copyright (c) 2011-2014, Synthetic Semantics LLC. All rights reserved. | + | Copyright (c) 2015-2016, Jace A Mogill. All rights reserved. | + | | + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the following conditions are met: | + | * Redistributions of source code must retain the above copyright | + | notice, this list of conditions and the following disclaimer. | + | * Redistributions in binary form must reproduce the above copyright | + | notice, this list of conditions and the following disclaimer in the | + | documentation and/or other materials provided with the distribution. | + | * Neither the name of the Synthetic Semantics nor the names of its | + | contributors may be used to endorse or promote products derived | + | from this software without specific prior written permission. | + | | + | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | + | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | + | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | + | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SYNTHETIC | + | SEMANTICS LLC BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | + | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | + | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | + | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | + | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | + | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | + | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | + | | + +-----------------------------------------------------------------------------*/ +#include "nodejs.h" +#include "ems.h" + + +void NodeJScriticalEnter(const Nan::FunctionCallbackInfo& info) { + NODE_MMAPID_DECL(); + int timeRemaining = EMScriticalEnter(mmapID, INT32_MAX); // TODO: Infinite wait time + if (timeRemaining <= 0) { + Nan::ThrowError("NodeJScriticalEnter: Unable to enter critical region before timeout"); + } else { + info.GetReturnValue().Set(Nan::New(true)); + } +} + + +void NodeJScriticalExit(const Nan::FunctionCallbackInfo& info) { + NODE_MMAPID_DECL(); + bool success = EMScriticalExit(mmapID); + if (!success) { + Nan::ThrowError("NodeJScriticalExit: critical region mutex lost while locked?!"); + } else { + info.GetReturnValue().Set(Nan::New(success)); + } +} + + +void NodeJSbarrier(const Nan::FunctionCallbackInfo& info) { + NODE_MMAPID_DECL(); + int timeRemaining = EMSbarrier(mmapID, INT32_MAX); // TODO: infinite wait time + if (timeRemaining <= 0) { + Nan::ThrowError("NodeJSbarrer: Failed to sync at barrier"); + } else { + info.GetReturnValue().Set(Nan::New(true)); + } +} + + +void NodeJSsingleTask(const Nan::FunctionCallbackInfo& info) { + NODE_MMAPID_DECL(); + bool did_work = EMSsingleTask(mmapID); + info.GetReturnValue().Set(Nan::New(did_work)); +} + + +void NodeJScas(const Nan::FunctionCallbackInfo& info) { + EMSvalueType returnValue; + NODE_KEY_DECL(NodeJScas, ); + NODE_INFO_DECL(NodeJScas, , oldValString, oldVal, 3, 1, false); + NODE_INFO_DECL(NodeJScas, , newValString, newVal, 3, 2, false); + bool success = EMScas(mmapID, &key, &oldVal, &newVal, &returnValue); + if (!success) { + Nan::ThrowError("NodeJScas: Failed to get a valid old value"); + return; + } + EMS_TO_V8_RETURNVALUE(returnValue, info, true); +} + + +void NodeJSfaa(const Nan::FunctionCallbackInfo& info) { + bool stringIsJSON = false; + EMSvalueType returnValue; + NODE_KEY_DECL(NodeJSfaa, ); + NODE_VALUE_DECL(NodeJSfaa, ); + bool success = EMSfaa(mmapID, &key, &value, &returnValue); + if (!success) { + Nan::ThrowError("NodeJSfaa: Failed to get a valid old value"); + return; + } + EMS_TO_V8_RETURNVALUE(returnValue, info, true); +} + + +void NodeJSpush(const Nan::FunctionCallbackInfo& info) { + NODE_PUSH_ENQUEUE(NodeJSpush, EMSpush); +} + + +void NodeJSpop(const Nan::FunctionCallbackInfo& info) { + EMSvalueType returnValue; + NODE_MMAPID_DECL(); + bool success = EMSpop(mmapID, &returnValue); + if (!success) { + Nan::ThrowError("NodeJSpop: Failed to pop a value off the stack"); + return; + } + EMS_TO_V8_RETURNVALUE(returnValue, info, true); +} + + +void NodeJSenqueue(const Nan::FunctionCallbackInfo& info) { + NODE_PUSH_ENQUEUE(NodeJSenqueue, EMSenqueue); +} + + +void NodeJSdequeue(const Nan::FunctionCallbackInfo& info) { + EMSvalueType returnValue; + NODE_MMAPID_DECL(); + bool success = EMSdequeue(mmapID, &returnValue); + if (!success) { + Nan::ThrowError("NodeJSdequeue: Failed to dequeue a value"); + return; + } + EMS_TO_V8_RETURNVALUE(returnValue, info, true); +} + + +void NodeJSloopInit(const Nan::FunctionCallbackInfo& info) { + NODE_MMAPID_DECL(); + if (info.Length() != 4) { + Nan::ThrowError("NodeJSloopInit: Wrong number of args"); + return; + } + + int32_t start = (int32_t) info[0]->ToInteger()->Value(); + int32_t end = (int32_t)info[1]->ToInteger()->Value(); + int schedule_mode; + std::string sched_string(*Nan::Utf8String(info[2])); + if (sched_string.compare("guided") == 0) { + schedule_mode = EMS_SCHED_GUIDED; + } else { + if (sched_string.compare("dynamic") == 0) { + schedule_mode = EMS_SCHED_DYNAMIC; + } else { + Nan::ThrowError("NodeJSloopInit: Unknown/invalid schedule mode"); + return; + } + } + int32_t minChunk = (int32_t) info[3]->ToInteger()->Value(); + + bool success = EMSloopInit(mmapID, start, end, minChunk, schedule_mode); + if (!success) { + Nan::ThrowError("NodeJSloopInit: Unknown failure to initalize loop"); + } else { + info.GetReturnValue().Set(Nan::New(success)); + } +} + + +void NodeJSloopChunk(const Nan::FunctionCallbackInfo& info) { + NODE_MMAPID_DECL(); + if (info.Length() != 0) { + Nan::ThrowError("NodeJSloopChunk: Arguments provided, but none accepted"); + return; + } + int32_t start, end; + EMSloopChunk(mmapID, &start, &end); // Unusued return value + + v8::Local retObj = Nan::New(); + retObj->Set(Nan::New("start").ToLocalChecked(), Nan::New(start)); + retObj->Set(Nan::New("end").ToLocalChecked(), Nan::New(end)); + info.GetReturnValue().Set(retObj); +} + + +//-------------------------------------------------------------- +void NodeJSread(const Nan::FunctionCallbackInfo& info) { + EMSvalueType returnValue; + NODE_READ(NodeJSread, EMSread, ); /* Bogus Missing argument warning */ +} + + +void NodeJSreadFE(const Nan::FunctionCallbackInfo& info) { + EMSvalueType returnValue; + NODE_READ(NodeJSreadFE, EMSreadFE, ); /* Bogus Missing argument warning */ +} + + +void NodeJSreadFF(const Nan::FunctionCallbackInfo& info) { + EMSvalueType returnValue; + NODE_READ(NodeJSreadFF, EMSreadFF, ); /* Bogus Missing argument warning */ +} + + +void NodeJSreadRW(const Nan::FunctionCallbackInfo& info) { + EMSvalueType returnValue; + NODE_READ(NodeJSreadRW, EMSreadRW, ); /* Bogus Missing argument warning */ +} + + +void NodeJSreleaseRW(const Nan::FunctionCallbackInfo& info) { + NODE_KEY_DECL(NodeJSreleaseRW, ); + int nReadersActive = EMSreleaseRW(mmapID, &key); + if (nReadersActive < 0) { + Nan::ThrowError("NodeJSreleaseRW: Invalid index for key, or index key in bad state"); + } else { + info.GetReturnValue().Set(Nan::New(nReadersActive)); + } +} + +// ==================================================== + +void NodeJSwrite(const Nan::FunctionCallbackInfo& info) { + NODE_WRITE(NodeJSwrite, EMSwrite); +} + + +void NodeJSwriteEF(const Nan::FunctionCallbackInfo& info) { + NODE_WRITE(NodeJSwriteEF, EMSwriteEF); +} + + +void NodeJSwriteXF(const Nan::FunctionCallbackInfo& info) { + NODE_WRITE(NodeJSwriteXF, EMSwriteXF); +} + + +void NodeJSwriteXE(const Nan::FunctionCallbackInfo& info) { + NODE_WRITE(NodeJSwriteXE, EMSwriteXE); +} + + +void NodeJSsetTag(const Nan::FunctionCallbackInfo& info) { + NODE_KEY_DECL(NodeJSsetTag, ); + bool is_full = info[1]->ToBoolean()->Value(); + bool success = EMSsetTag(mmapID, &key, is_full); + if(success) { + info.GetReturnValue().Set(Nan::New(true)); + } else { + Nan::ThrowError("NodeJSsetTag: Invalid key, unable to set tag"); + } +} + + +void NodeJSsync(const Nan::FunctionCallbackInfo& info) { + NODE_MMAPID_DECL(); + fprintf(stderr, "NodeJSsync: WARNING: sync is not implemented\n"); + bool success = EMSsync(mmapID); + info.GetReturnValue().Set(Nan::New(success)); +} + + +void NodeJSindex2key(const Nan::FunctionCallbackInfo& info) { + NODE_MMAPID_DECL(); + EMSvalueType key; + int idx = (int32_t) info[0]->ToInteger()->Value(); // TODO: This is just 32bit, should be size_t + if( !EMSindex2key(mmapID, idx, &key) ) { + fprintf(stderr, "NodeJSindex2key: Error converting index to key\n"); + } + EMS_TO_V8_RETURNVALUE(key, info, false); +} + + +void NodeJSdestroy(const Nan::FunctionCallbackInfo& info) { + NODE_MMAPID_DECL(); + bool do_unlink = info[0]->ToBoolean()->Value(); + bool success = EMSdestroy(mmapID, do_unlink); + if (success) { + info.GetReturnValue().Set(Nan::New(true)); + } else { + Nan::ThrowError("NodeJSdestroy: Failed to destroy EMS array"); + } +}; + + +//================================================================== +// EMS Entry Point: Allocate and initialize the EMS domain memory +void NodeJSinitialize(const Nan::FunctionCallbackInfo& info) { + if (info.Length() != 15) { + Nan::ThrowError("NodeJSinitialize: Incorrect number of arguments"); + return; + } + EMSvalueType fillData; + std::string fillString; + + // Parse all the arguments + int64_t nElements = info[0]->ToInteger()->Value(); + int64_t heapSize = info[1]->ToInteger()->Value(); + bool useMap = info[2]->ToBoolean()->Value(); + std::string filestring(*Nan::Utf8String(info[3])); + const char *filename = filestring.c_str(); + bool persist = info[4]->ToBoolean()->Value(); + bool useExisting = info[5]->ToBoolean()->Value(); + bool doDataFill = info[6]->ToBoolean()->Value(); + bool fillIsJSON = info[7]->ToBoolean()->Value(); + // 8 = Data Fill type TBD during fill + bool doSetFEtags = info[9]->ToBoolean()->Value(); + bool setFEtags = info[10]->ToBoolean()->Value(); + EMSmyID = (int) info[11]->ToInteger()->Value(); + bool pinThreads = info[12]->ToBoolean()->Value(); + int64_t nThreads = info[13]->ToInteger()->Value(); + int64_t pctMLock = info[14]->ToInteger()->Value(); + + if(doDataFill) { + NAN_OBJ_TO_EMS_VAL(NodeJSinitialize, info[8], fillData, fillString, fillIsJSON); + } + + int emsBufN = EMSinitialize(nElements, // 0 + heapSize, // 1 + useMap, // 2 + filename, // 3 + persist, // 4 + useExisting,// 5 + doDataFill, // 6 Data Fill type TBD during fill + fillIsJSON, // 7 + fillData, // 8 + doSetFEtags,// 9 + setFEtags, // 10 + EMSmyID, // 11 + pinThreads, // 12 + nThreads, // 13 + pctMLock); // 14 + + if(emsBufN < 0) { + Nan::ThrowError("NodeJSinitialize: failed to initialize EMS array"); + return; + } + + // ======================================================================================== + v8::Local obj = Nan::New(); + obj->Set(Nan::New("mmapID").ToLocalChecked(), Nan::New(emsBufN)); + ADD_FUNC_TO_V8_OBJ(obj, "faa", NodeJSfaa); + ADD_FUNC_TO_V8_OBJ(obj, "cas", NodeJScas); + ADD_FUNC_TO_V8_OBJ(obj, "read", NodeJSread); + ADD_FUNC_TO_V8_OBJ(obj, "write", NodeJSwrite); + ADD_FUNC_TO_V8_OBJ(obj, "readRW", NodeJSreadRW); + ADD_FUNC_TO_V8_OBJ(obj, "releaseRW", NodeJSreleaseRW); + ADD_FUNC_TO_V8_OBJ(obj, "readFE", NodeJSreadFE); + ADD_FUNC_TO_V8_OBJ(obj, "readFF", NodeJSreadFF); + ADD_FUNC_TO_V8_OBJ(obj, "setTag", NodeJSsetTag); + ADD_FUNC_TO_V8_OBJ(obj, "writeEF", NodeJSwriteEF); + ADD_FUNC_TO_V8_OBJ(obj, "writeXF", NodeJSwriteXF); + ADD_FUNC_TO_V8_OBJ(obj, "writeXE", NodeJSwriteXE); + ADD_FUNC_TO_V8_OBJ(obj, "push", NodeJSpush); + ADD_FUNC_TO_V8_OBJ(obj, "pop", NodeJSpop); + ADD_FUNC_TO_V8_OBJ(obj, "enqueue", NodeJSenqueue); + ADD_FUNC_TO_V8_OBJ(obj, "dequeue", NodeJSdequeue); + ADD_FUNC_TO_V8_OBJ(obj, "sync", NodeJSsync); + ADD_FUNC_TO_V8_OBJ(obj, "index2key", NodeJSindex2key); + ADD_FUNC_TO_V8_OBJ(obj, "destroy", NodeJSdestroy); + info.GetReturnValue().Set(obj); +} + + +//--------------------------------------------------------------- +static void RegisterModule(v8::Handle target) { + ADD_FUNC_TO_V8_OBJ(target, "initialize", NodeJSinitialize); + ADD_FUNC_TO_V8_OBJ(target, "barrier", NodeJSbarrier); + ADD_FUNC_TO_V8_OBJ(target, "singleTask", NodeJSsingleTask); + ADD_FUNC_TO_V8_OBJ(target, "criticalEnter", NodeJScriticalEnter); + ADD_FUNC_TO_V8_OBJ(target, "criticalExit", NodeJScriticalExit); + ADD_FUNC_TO_V8_OBJ(target, "loopInit", NodeJSloopInit); + ADD_FUNC_TO_V8_OBJ(target, "loopChunk", NodeJSloopChunk); +} + + +NODE_MODULE(ems, RegisterModule); diff --git a/nodejs.h b/nodejs.h new file mode 100644 index 0000000..0a2944d --- /dev/null +++ b/nodejs.h @@ -0,0 +1,233 @@ +/*-----------------------------------------------------------------------------+ + | Extended Memory Semantics (EMS) Version 1.3.0 | + | Synthetic Semantics http://www.synsem.com/ mogill@synsem.com | + +-----------------------------------------------------------------------------+ + | Copyright (c) 2011-2014, Synthetic Semantics LLC. All rights reserved. | + | Copyright (c) 2015-2016, Jace A Mogill. All rights reserved. | + | | + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the following conditions are met: | + | * Redistributions of source code must retain the above copyright | + | notice, this list of conditions and the following disclaimer. | + | * Redistributions in binary form must reproduce the above copyright | + | notice, this list of conditions and the following disclaimer in the | + | documentation and/or other materials provided with the distribution. | + | * Neither the name of the Synthetic Semantics nor the names of its | + | contributors may be used to endorse or promote products derived | + | from this software without specific prior written permission. | + | | + | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | + | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | + | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | + | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SYNTHETIC | + | SEMANTICS LLC BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | + | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | + | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | + | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | + | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | + | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | + | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | + | | + +-----------------------------------------------------------------------------*/ +#ifndef EMSPROJ_NODEJS_H +#define EMSPROJ_NODEJS_H +#include +#include +#include "nan.h" + +#define ADD_FUNC_TO_V8_OBJ(obj, func_name, func) \ + { \ + v8::Local tpl = Nan::New(func); \ + v8::Local fn = tpl->GetFunction(); \ + fn->SetName(Nan::New(func_name).ToLocalChecked()); \ + obj->Set(Nan::New(func_name).ToLocalChecked(), tpl->GetFunction()); \ + } + +//================================================================== +// Determine the EMS type of a V8 argument +#define NanObjToEMStype(arg, stringIsJSON) \ +( \ + arg->IsInt32() ? EMS_TYPE_INTEGER : \ + arg->IsNumber() ? EMS_TYPE_FLOAT : \ + (arg->IsString() && !stringIsJSON) ? EMS_TYPE_STRING : \ + (arg->IsString() && stringIsJSON) ? EMS_TYPE_JSON : \ + arg->IsBoolean() ? EMS_TYPE_BOOLEAN : \ + arg->IsUndefined() ? EMS_TYPE_UNDEFINED: \ + arg->IsUint32() ? EMS_TYPE_INTEGER : EMS_TYPE_INVALID \ +) + + +#define NAN_OBJ_TO_EMS_VAL(funcname, info, localValue, argString, stringIsJSON) { \ + localValue.type = NanObjToEMStype(info, stringIsJSON); \ + switch (localValue.type) { \ + case EMS_TYPE_BOOLEAN: \ + localValue.value = (void *) info->ToBoolean()->Value(); \ + break; \ + case EMS_TYPE_INTEGER: \ + localValue.value = (void *) info->ToInteger()->Value(); \ + break; \ + case EMS_TYPE_FLOAT: { \ + ulong_double alias; \ + alias.d = info->ToNumber()->Value(); \ + localValue.value = (void *) alias.u64; \ + } \ + break; \ + case EMS_TYPE_JSON: \ + case EMS_TYPE_STRING: { \ + argString = std::string(*Nan::Utf8String(info)); \ + localValue.value = (void *) argString.c_str(); \ + } \ + break; \ + case EMS_TYPE_UNDEFINED: \ + localValue.value = (void *) 0xbeeff00d; \ + break; \ + default: \ + Nan::ThrowTypeError(#funcname " ERROR: Invalid value type"); \ + return; \ + } \ +} + + +#define EMS_TO_V8_RETURNVALUE(returnValue, info, do_free) { \ + switch(returnValue.type) { \ + case EMS_TYPE_BOOLEAN: { \ + bool retBool = (bool) returnValue.value; \ + info.GetReturnValue().Set(Nan::New(retBool)); \ + } \ + break; \ + case EMS_TYPE_INTEGER: { \ + int32_t retInt = ((int64_t) returnValue.value) & 0xffffffff; /* TODO: Bug -- only 32 bits of 64? */ \ + info.GetReturnValue().Set(Nan::New(retInt)); \ + } \ + break; \ + case EMS_TYPE_FLOAT: { \ + ulong_double alias; \ + alias.u64 = (uint64_t) returnValue.value; \ + info.GetReturnValue().Set(Nan::New(alias.d)); \ + } \ + break; \ + case EMS_TYPE_JSON: { \ + v8::Local retObj = Nan::New(/* Bogus Missing Arg Warning Here */); \ + retObj->Set(Nan::New("data").ToLocalChecked(), \ + Nan::New((char *) returnValue.value).ToLocalChecked()); \ + info.GetReturnValue().Set(retObj); \ + if (do_free) free(returnValue.value); /* Allocated in ems.cc (cas, faa, pop, dequeue ...) */ \ + } \ + break; \ + case EMS_TYPE_STRING: { \ + info.GetReturnValue().Set(Nan::New((char *) returnValue.value).ToLocalChecked()); \ + if (do_free) free(returnValue.value); /* Allocated in ems.cc (cas, faa, pop, dequeue...) */ \ + } \ + break; \ + case EMS_TYPE_UNDEFINED: { \ + info.GetReturnValue().Set(Nan::Undefined()); \ + } \ + break; \ + default: \ + Nan::ThrowTypeError("EMS ERROR: EMS_TO_V8_RETURNVALUE: Invalid type of data read from memory"); \ + } \ +} + +//================================================================== +// Macro to declare and unwrap the EMS buffer, used to access the +// EMSarray object metadata +#define JS_ARG_TO_OBJ(arg) v8::Handle::Cast(arg) +#define JS_PROP_TO_VALUE(obj, property) JS_ARG_TO_OBJ(obj)->Get(Nan::New(property).ToLocalChecked()) +#define JS_PROP_TO_INT(obj, property) (JS_PROP_TO_VALUE(obj, property)->ToInteger()->Value()) + +#define NODE_MMAPID_DECL() \ + const int mmapID = JS_PROP_TO_INT(info.This(), "mmapID") + +#define NODE_INFO_DECL(nodejs_funcname, retval, valueString, value, expectedNargs, infoArgN, stringIsJSON) \ + std::string valueString; \ + EMSvalueType value; \ + if (infoArgN >= info.Length()) { \ + Nan::ThrowError(#nodejs_funcname ": Called with wrong number of arguments."); \ + return retval; \ + } \ + NAN_OBJ_TO_EMS_VAL(nodejs_funcname, info[infoArgN], value, valueString, stringIsJSON) + +#define NODE_WRITE(nodejs_funcname, ems_funcname) \ + bool stringIsJSON = false; \ + NODE_MMAPID_DECL(); \ + if (info.Length() == 3) { \ + stringIsJSON = info[2]->ToBoolean()->Value(); \ + } else { \ + if (info.Length() != 2) { \ + Nan::ThrowError(#nodejs_funcname ": Called with wrong number of args."); \ + return; \ + } \ + } \ + \ + NODE_INFO_DECL(nodejs_funcname, , keyString, key, 2, 0, false); \ + NODE_INFO_DECL(nodejs_funcname, , valueString, value, 2, 1, stringIsJSON); \ + \ + bool returnValue = ems_funcname (mmapID, &key, &value); \ + info.GetReturnValue().Set(Nan::New(returnValue)); + +#define NODE_PUSH_ENQUEUE(nodejs_funcname, ems_funcname) \ + bool stringIsJSON = false; \ + NODE_MMAPID_DECL(); \ + if (info.Length() == 2) { \ + stringIsJSON = info[1]->ToBoolean()->Value(); \ + } else { \ + if (info.Length() != 1) { \ + Nan::ThrowError(#nodejs_funcname ": Called with wrong number of args."); \ + return; \ + } \ + } \ + NODE_INFO_DECL(nodejs_funcname, , valueString, value, 1, 0, stringIsJSON); \ + int returnValue = ems_funcname (mmapID, &value); \ + info.GetReturnValue().Set(Nan::New(returnValue)); + +#define NODE_VALUE_DECL(nodejs_funcname, retval) \ + NODE_INFO_DECL(nodejs_funcname, retval, valueString, value, 1, 1, stringIsJSON) + +#define NODE_KEY_DECL(nodejs_funcname, retval) \ + NODE_MMAPID_DECL(); \ + std::string keyString; \ + EMSvalueType key; \ + if (info.Length() < 1) { \ + Nan::ThrowError(#nodejs_funcname ": Called with wrong number of args."); \ + return retval; \ + } \ + NAN_OBJ_TO_EMS_VAL(nodejs_funcname, info[0], key, keyString, false) + +#define NODE_READ(nodejs_funcname, ems_funcname, retval) \ + NODE_KEY_DECL(nodejs_funcname, retval) \ + bool errval = ems_funcname (mmapID, &key, &returnValue); \ + if(errval == false) { \ + Nan::ThrowError(#nodejs_funcname ": Unable to read (no return value) from EMS."); \ + return retval; \ + } \ + EMS_TO_V8_RETURNVALUE(returnValue, info, false) /* Bogus Missing argument warning */ + + +void NodeJScriticalEnter(const Nan::FunctionCallbackInfo& info); +void NodeJScriticalExit(const Nan::FunctionCallbackInfo& info); +void NodeJSbarrier(const Nan::FunctionCallbackInfo& info); +void NodeJSsingleTask(const Nan::FunctionCallbackInfo& info); +void NodeJScas(const Nan::FunctionCallbackInfo &info); +void NodeJSfaa(const Nan::FunctionCallbackInfo& info); +void NodeJSpush(const Nan::FunctionCallbackInfo &info); +void NodeJSpop(const Nan::FunctionCallbackInfo &info); +void NodeJSenqueue(const Nan::FunctionCallbackInfo &info); +void NodeJSdequeue(const Nan::FunctionCallbackInfo &info); +void NodeJSloopInit(const Nan::FunctionCallbackInfo& info); +void NodeJSloopChunk(const Nan::FunctionCallbackInfo& info); +//-------------------------------------------------------------- +void NodeJSread(const Nan::FunctionCallbackInfo& info); +void NodeJSreadRW(const Nan::FunctionCallbackInfo& info); +void NodeJSreleaseRW(const Nan::FunctionCallbackInfo& info); +void NodeJSreadFE(const Nan::FunctionCallbackInfo& info); +void NodeJSreadFF(const Nan::FunctionCallbackInfo& info); +void NodeJSwrite(const Nan::FunctionCallbackInfo& info); +void NodeJSwriteEF(const Nan::FunctionCallbackInfo& info); +void NodeJSwriteXF(const Nan::FunctionCallbackInfo& info); +void NodeJSwriteXE(const Nan::FunctionCallbackInfo& info); +void NodeJSsetTag(const Nan::FunctionCallbackInfo& info); +void NodeJSsync(const Nan::FunctionCallbackInfo& info); +void NodeJSindex2key(const Nan::FunctionCallbackInfo& info); +void NodeJSdestroy(const Nan::FunctionCallbackInfo& info); + +#endif //EMSPROJ__H diff --git a/package.json b/package.json index f6e8f15..3e61342 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ems", - "version": "1.2.0", + "version": "1.3.0", "author": "Synthetic Semantics ", "description": "Shared Memory Parallelism with Transactional Memory and Extended Memory Semantics", "contributors": [ @@ -20,6 +20,9 @@ }, "homepage": "https://synsem.com/EMS.js", "keywords": [ + "non volatile memory", + "NVM", + "NVMe", "multithreading", "multithreaded", "parallel", diff --git a/primitives.cc b/primitives.cc index 3dbdb13..91a6bd2 100644 --- a/primitives.cc +++ b/primitives.cc @@ -1,5 +1,5 @@ /*-----------------------------------------------------------------------------+ - | Extended Memory Semantics (EMS) Version 1.0.0 | + | Extended Memory Semantics (EMS) Version 1.3.0 | | Synthetic Semantics http://www.synsem.com/ mogill@synsem.com | +-----------------------------------------------------------------------------+ | Copyright (c) 2011-2014, Synthetic Semantics LLC. All rights reserved. | @@ -31,64 +31,52 @@ +-----------------------------------------------------------------------------*/ #include "ems.h" + //================================================================== // Push onto stack -// -void EMSpush(const Nan::FunctionCallbackInfo &info) { - THIS_INFO_TO_EMSBUF(info, "mmapID"); +int EMSpush(int mmapID, EMSvalueType *value) { // TODO: Eventually promote return value to 64bit + void *emsBuf = emsBufs[mmapID]; int64_t *bufInt64 = (int64_t *) emsBuf; EMStag_t *bufTags = (EMStag_t *) emsBuf; - double *bufDouble = (double *) emsBuf; char *bufChar = (char *) emsBuf; EMStag_t newTag; - int stringIsJSON = false; - - if (info.Length() == 2) { - stringIsJSON = info[1]->ToBoolean()->Value(); - } // Wait until the stack top is full, then mark it busy while updating the stack EMStransitionFEtag(&bufTags[EMScbTag(EMS_ARR_STACKTOP)], NULL, EMS_TAG_FULL, EMS_TAG_BUSY, EMS_TAG_ANY); int32_t idx = bufInt64[EMScbData(EMS_ARR_STACKTOP)]; // TODO BUG: Truncating the full 64b range bufInt64[EMScbData(EMS_ARR_STACKTOP)]++; if (idx == bufInt64[EMScbData(EMS_ARR_NELEM)] - 1) { - Nan::ThrowError("EMSpush: Ran out of stack entries"); - return; + fprintf(stderr, "EMSpush: Ran out of stack entries\n"); + return -1; } // Wait until the target memory at the top of the stack is empty newTag.byte = EMStransitionFEtag(&bufTags[EMSdataTag(idx)], NULL, EMS_TAG_EMPTY, EMS_TAG_BUSY, EMS_TAG_ANY); newTag.tags.rw = 0; - newTag.tags.type = EMSv8toEMStype(info[0], stringIsJSON); + newTag.tags.type = value->type; newTag.tags.fe = EMS_TAG_FULL; // Write the value onto the stack switch (newTag.tags.type) { case EMS_TYPE_BOOLEAN: - bufInt64[EMSdataData(idx)] = (int64_t) info[0]->ToBoolean()->Value(); - break; case EMS_TYPE_INTEGER: - bufInt64[EMSdataData(idx)] = (int64_t) info[0]->ToInteger()->Value(); - break; case EMS_TYPE_FLOAT: - bufDouble[EMSdataData(idx)] = (double) info[0]->ToNumber()->Value(); + bufInt64[EMSdataData(idx)] = (int64_t) value->value; break; case EMS_TYPE_JSON: case EMS_TYPE_STRING: { - std::string argString(*Nan::Utf8String(info[0])); - const char *arg_c_str = argString.c_str(); int64_t textOffset; - EMS_ALLOC(textOffset, argString.length() + 1, "EMSpush: out of memory to store string", ); + EMS_ALLOC(textOffset, strlen((const char *) value->value) + 1, bufChar, "EMSpush: out of memory to store string\n", -1); bufInt64[EMSdataData(idx)] = textOffset; - strcpy(EMSheapPtr(textOffset), arg_c_str); + strcpy(EMSheapPtr(textOffset), (const char *) value->value); } break; case EMS_TYPE_UNDEFINED: bufInt64[EMSdataData(idx)] = 0xdeadbeef; break; default: - Nan::ThrowTypeError("EMSpush: Unknown arg type"); - return; + fprintf(stderr, "EMSpush: Unknown value type\n"); + return -1; } // Mark the data on the stack as FULL @@ -97,19 +85,17 @@ void EMSpush(const Nan::FunctionCallbackInfo &info) { // Push is complete, Mark the stack pointer as full bufTags[EMScbTag(EMS_ARR_STACKTOP)].tags.fe = EMS_TAG_FULL; - info.GetReturnValue().Set(Nan::New(idx)); - return; + return idx; } //================================================================== // Pop data from stack // -void EMSpop(const Nan::FunctionCallbackInfo &info) { - THIS_INFO_TO_EMSBUF(info, "mmapID"); +bool EMSpop(int mmapID, EMSvalueType *returnValue) { + void *emsBuf = emsBufs[mmapID]; int64_t *bufInt64 = (int64_t *) emsBuf; EMStag_t *bufTags = (EMStag_t *) emsBuf; - double *bufDouble = (double *) emsBuf; char *bufChar = (char *) emsBuf; EMStag_t dataTag; @@ -121,63 +107,46 @@ void EMSpop(const Nan::FunctionCallbackInfo &info) { // Stack is empty, return undefined bufInt64[EMScbData(EMS_ARR_STACKTOP)] = 0; bufTags[EMScbTag(EMS_ARR_STACKTOP)].tags.fe = EMS_TAG_FULL; - info.GetReturnValue().Set(Nan::Undefined()); - return; + returnValue->type = EMS_TYPE_UNDEFINED; + returnValue->value = (void *) 0xf00dd00f; + return true; } - // Wait until the data pointed to by the stack pointer is full, then mark it // busy while it is copied, and set it to EMPTY when finished dataTag.byte = EMStransitionFEtag(&bufTags[EMSdataTag(idx)], NULL, EMS_TAG_FULL, EMS_TAG_BUSY, EMS_TAG_ANY); + returnValue->type = dataTag.tags.type; switch (dataTag.tags.type) { - case EMS_TYPE_BOOLEAN: { - bool retBool = bufInt64[EMSdataData(idx)]; - bufTags[EMSdataTag(idx)].tags.fe = EMS_TAG_EMPTY; - bufTags[EMScbTag(EMS_ARR_STACKTOP)].tags.fe = EMS_TAG_FULL; - info.GetReturnValue().Set(Nan::New(retBool)); - return; - } - case EMS_TYPE_INTEGER: { - int32_t retInt = bufInt64[EMSdataData(idx)]; // TODO: BUG 64b truncation again - bufTags[EMSdataTag(idx)].tags.fe = EMS_TAG_EMPTY; - bufTags[EMScbTag(EMS_ARR_STACKTOP)].tags.fe = EMS_TAG_FULL; - info.GetReturnValue().Set(Nan::New(retInt)); - return; - } + case EMS_TYPE_BOOLEAN: + case EMS_TYPE_INTEGER: case EMS_TYPE_FLOAT: { - double retFloat = bufDouble[EMSdataData(idx)]; + returnValue->value = (void *) bufInt64[EMSdataData(idx)]; bufTags[EMSdataTag(idx)].tags.fe = EMS_TAG_EMPTY; bufTags[EMScbTag(EMS_ARR_STACKTOP)].tags.fe = EMS_TAG_FULL; - info.GetReturnValue().Set(Nan::New(retFloat)); - return; + return true; } case EMS_TYPE_JSON: case EMS_TYPE_STRING: { + size_t memStrLen = strlen(EMSheapPtr(bufInt64[EMSdataData(idx)])); // TODO: Use size of allocation, not strlen + returnValue->value = malloc(memStrLen + 1); // freed in NodeJSfaa + if(returnValue->value == NULL) { + fprintf(stderr, "EMSpop: Unable to allocate space to return stack top string\n"); + return false; + } + strcpy((char *) returnValue->value, EMSheapPtr(bufInt64[EMSdataData(idx)])); + EMS_FREE(bufInt64[EMSdataData(idx)]); bufTags[EMSdataTag(idx)].tags.fe = EMS_TAG_EMPTY; bufTags[EMScbTag(EMS_ARR_STACKTOP)].tags.fe = EMS_TAG_FULL; - if (dataTag.tags.type == EMS_TYPE_JSON) { - v8::Local retObj = Nan::New(); - retObj->Set(Nan::New("data").ToLocalChecked(), - // v8::String::NewFromUtf8(isolate, EMSheapPtr(bufInt64[EMSdataData(idx)]))); - Nan::New(EMSheapPtr(bufInt64[EMSdataData(idx)])).ToLocalChecked()); - EMS_FREE(bufInt64[EMSdataData(idx)]); - info.GetReturnValue().Set(retObj); - return; - } else { - // info.GetReturnValue().Set(v8::String::NewFromUtf8(isolate, EMSheapPtr(bufInt64[EMSdataData(idx)]))); - info.GetReturnValue().Set(Nan::New(EMSheapPtr(bufInt64[EMSdataData(idx)])).ToLocalChecked()); - EMS_FREE(bufInt64[EMSdataData(idx)]); - return; - } + return true; } case EMS_TYPE_UNDEFINED: { bufTags[EMSdataTag(idx)].tags.fe = EMS_TAG_EMPTY; bufTags[EMScbTag(EMS_ARR_STACKTOP)].tags.fe = EMS_TAG_FULL; - info.GetReturnValue().Set(Nan::Undefined()); - return; + returnValue->value = (void *) 0xdeadbeef; + return true; } default: - Nan::ThrowTypeError("EMSpop unknown type"); - return; + fprintf(stderr, "EMSpop: ERROR - unknown top of stack data type\n"); + return false; } } @@ -186,57 +155,45 @@ void EMSpop(const Nan::FunctionCallbackInfo &info) { // Enqueue data // Heap top and bottom are monotonically increasing, but the index // returned is a circular buffer. -// -void EMSenqueue(const Nan::FunctionCallbackInfo &info) { - THIS_INFO_TO_EMSBUF(info, "mmapID"); +int EMSenqueue(int mmapID, EMSvalueType *value) { // TODO: Eventually promote return value to 64bit + void *emsBuf = emsBufs[mmapID]; int64_t *bufInt64 = (int64_t *) emsBuf; EMStag_t *bufTags = (EMStag_t *) emsBuf; - double *bufDouble = (double *) emsBuf; char *bufChar = (char *) emsBuf; - int stringIsJSON = false; - if (info.Length() == 2) { - stringIsJSON = info[1]->ToBoolean()->Value(); - } // Wait until the heap top is full, and mark it busy while data is enqueued EMStransitionFEtag(&bufTags[EMScbTag(EMS_ARR_STACKTOP)], NULL, EMS_TAG_FULL, EMS_TAG_BUSY, EMS_TAG_ANY); - int32_t idx = bufInt64[EMScbData(EMS_ARR_STACKTOP)] % bufInt64[EMScbData(EMS_ARR_NELEM)]; // TODO: BUG This could be trucated + int32_t idx = bufInt64[EMScbData(EMS_ARR_STACKTOP)] % bufInt64[EMScbData(EMS_ARR_NELEM)]; // TODO: BUG This could be truncated bufInt64[EMScbData(EMS_ARR_STACKTOP)]++; if (bufInt64[EMScbData(EMS_ARR_STACKTOP)] - bufInt64[EMScbData(EMS_ARR_Q_BOTTOM)] > bufInt64[EMScbData(EMS_ARR_NELEM)]) { - Nan::ThrowError("EMSenqueue: Ran out of stack entries"); - return; + fprintf(stderr, "EMSenqueue: Ran out of stack entries\n"); + return -1; } // Wait for data pointed to by heap top to be empty, then set to Full while it is filled bufTags[EMSdataTag(idx)].tags.rw = 0; - bufTags[EMSdataTag(idx)].tags.type = EMSv8toEMStype(info[0], stringIsJSON); + bufTags[EMSdataTag(idx)].tags.type = value->type; switch (bufTags[EMSdataTag(idx)].tags.type) { case EMS_TYPE_BOOLEAN: - bufInt64[EMSdataData(idx)] = (int64_t) info[0]->ToBoolean()->Value(); - break; case EMS_TYPE_INTEGER: - bufInt64[EMSdataData(idx)] = (int64_t) info[0]->ToInteger()->Value(); - break; case EMS_TYPE_FLOAT: - bufDouble[EMSdataData(idx)] = (double) info[0]->ToNumber()->Value(); + bufInt64[EMSdataData(idx)] = (int64_t) value->value; break; case EMS_TYPE_JSON: case EMS_TYPE_STRING: { - std::string argString(*Nan::Utf8String(info[0])); - const char *arg_c_str = argString.c_str(); int64_t textOffset; - EMS_ALLOC(textOffset, argString.length() + 1, "EMSenqueue: out of memory to store string", ); + EMS_ALLOC(textOffset, strlen((const char *) value->value) + 1, bufChar, "EMSenqueue: out of memory to store string\n", -1); bufInt64[EMSdataData(idx)] = textOffset; - strcpy(EMSheapPtr(textOffset), arg_c_str); + strcpy(EMSheapPtr(textOffset), (const char *) value->value); } break; case EMS_TYPE_UNDEFINED: bufInt64[EMSdataData(idx)] = 0xdeadbeef; break; default: - Nan::ThrowTypeError("xEMSwrite: Unknown arg type"); - return; + fprintf(stderr, "EMSenqueue: Unknown value type\n"); + return -1; } // Set the tag on the data to FULL @@ -244,18 +201,16 @@ void EMSenqueue(const Nan::FunctionCallbackInfo &info) { // Enqueue is complete, set the tag on the heap to to FULL bufTags[EMScbTag(EMS_ARR_STACKTOP)].tags.fe = EMS_TAG_FULL; - info.GetReturnValue().Set(Nan::New(idx)); - return; + return idx; } //================================================================== // Dequeue -void EMSdequeue(const Nan::FunctionCallbackInfo &info) { - THIS_INFO_TO_EMSBUF(info, "mmapID"); +bool EMSdequeue(int mmapID, EMSvalueType *returnValue) { + void *emsBuf = emsBufs[mmapID]; int64_t *bufInt64 = (int64_t *) emsBuf; EMStag_t *bufTags = (EMStag_t *) emsBuf; - double *bufDouble = (double *) emsBuf; char *bufChar = (char *) emsBuf; EMStag_t dataTag; @@ -266,8 +221,9 @@ void EMSdequeue(const Nan::FunctionCallbackInfo &info) { if (bufInt64[EMScbData(EMS_ARR_Q_BOTTOM)] >= bufInt64[EMScbData(EMS_ARR_STACKTOP)]) { bufInt64[EMScbData(EMS_ARR_Q_BOTTOM)] = bufInt64[EMScbData(EMS_ARR_STACKTOP)]; bufTags[EMScbTag(EMS_ARR_Q_BOTTOM)].tags.fe = EMS_TAG_FULL; - info.GetReturnValue().Set(Nan::Undefined()); - return; + returnValue->type = EMS_TYPE_UNDEFINED; + returnValue->value = (void *) 0xf00dd00f; + return true; } bufInt64[EMScbData(EMS_ARR_Q_BOTTOM)]++; @@ -275,54 +231,38 @@ void EMSdequeue(const Nan::FunctionCallbackInfo &info) { // then mark busy while copying it, and finally set it to empty when done dataTag.byte = EMStransitionFEtag(&bufTags[EMSdataTag(idx)], NULL, EMS_TAG_FULL, EMS_TAG_BUSY, EMS_TAG_ANY); dataTag.tags.fe = EMS_TAG_EMPTY; + returnValue->type = dataTag.tags.type; switch (dataTag.tags.type) { - case EMS_TYPE_BOOLEAN: { - bool retBool = bufInt64[EMSdataData(idx)]; - bufTags[EMSdataTag(idx)].byte = dataTag.byte; - bufTags[EMScbTag(EMS_ARR_Q_BOTTOM)].tags.fe = EMS_TAG_FULL; - info.GetReturnValue().Set(Nan::New(retBool)); - return; - } - case EMS_TYPE_INTEGER: { - int32_t retInt = bufInt64[EMSdataData(idx)]; - bufTags[EMSdataTag(idx)].byte = dataTag.byte; - bufTags[EMScbTag(EMS_ARR_Q_BOTTOM)].tags.fe = EMS_TAG_FULL; - info.GetReturnValue().Set(Nan::New(retInt)); - return; - } + case EMS_TYPE_BOOLEAN: + case EMS_TYPE_INTEGER: case EMS_TYPE_FLOAT: { - double retFloat = bufDouble[EMSdataData(idx)]; + returnValue->value = (void *) bufInt64[EMSdataData(idx)]; bufTags[EMSdataTag(idx)].byte = dataTag.byte; bufTags[EMScbTag(EMS_ARR_Q_BOTTOM)].tags.fe = EMS_TAG_FULL; - info.GetReturnValue().Set(Nan::New(retFloat)); - return; + return true; } case EMS_TYPE_JSON: case EMS_TYPE_STRING: { bufTags[EMSdataTag(idx)].byte = dataTag.byte; bufTags[EMScbTag(EMS_ARR_Q_BOTTOM)].tags.fe = EMS_TAG_FULL; - if (dataTag.tags.type == EMS_TYPE_JSON) { - v8::Local retObj = Nan::New(); - retObj->Set(Nan::New("data").ToLocalChecked(), - Nan::New(EMSheapPtr(bufInt64[EMSdataData(idx)])).ToLocalChecked()); - EMS_FREE(bufInt64[EMSdataData(idx)]); - info.GetReturnValue().Set(retObj); - return; - } else { - // info.GetReturnValue().Set(v8::String::NewFromUtf8(isolate, EMSheapPtr(bufInt64[EMSdataData(idx)]))); - info.GetReturnValue().Set(Nan::New(EMSheapPtr(bufInt64[EMSdataData(idx)])).ToLocalChecked()); - EMS_FREE(bufInt64[EMSdataData(idx)]); - return; + size_t memStrLen = strlen(EMSheapPtr(bufInt64[EMSdataData(idx)])); // TODO: Use size of allocation, not strlen + returnValue->value = malloc(memStrLen + 1); // freed in NodeJSfaa + if(returnValue->value == NULL) { + fprintf(stderr, "EMSdequeue: Unable to allocate space to return queue head string\n"); + return false; } + strcpy((char *) returnValue->value, EMSheapPtr(bufInt64[EMSdataData(idx)])); + EMS_FREE(bufInt64[EMSdataData(idx)]); + return true; } case EMS_TYPE_UNDEFINED: { bufTags[EMSdataTag(idx)].byte = dataTag.byte; bufTags[EMScbTag(EMS_ARR_Q_BOTTOM)].tags.fe = EMS_TAG_FULL; - info.GetReturnValue().Set(Nan::Undefined()); - return; + returnValue->value = (void *) 0xdeadbeef; + return true; } default: - Nan::ThrowTypeError("EMSdequeue unknown type"); - return; + fprintf(stderr, "EMSdequeue: ERROR - unknown type at head of queue\n"); + return false; } } diff --git a/rmw.cc b/rmw.cc index 6141dac..6131a04 100644 --- a/rmw.cc +++ b/rmw.cc @@ -1,5 +1,5 @@ /*-----------------------------------------------------------------------------+ - | Extended Memory Semantics (EMS) Version 1.0.0 | + | Extended Memory Semantics (EMS) Version 1.3.0 | | Synthetic Semantics http://www.synsem.com/ mogill@synsem.com | +-----------------------------------------------------------------------------+ | Copyright (c) 2011-2014, Synthetic Semantics LLC. All rights reserved. | @@ -34,445 +34,403 @@ //================================================================== // Fetch and Add Atomic Memory Operation // Returns a+b where a is data in EMS memory and b is an argument -// -void EMSfaa(const Nan::FunctionCallbackInfo& info) { - THIS_INFO_TO_EMSBUF(info, "mmapID"); - EMStag_t *bufTags = (EMStag_t *) emsBuf; +bool EMSfaa(int mmapID, EMSvalueType *key, EMSvalueType *value, EMSvalueType *returnValue) { + void *emsBuf = emsBufs[mmapID]; + volatile EMStag_t *bufTags = (EMStag_t *) emsBuf; + int64_t idx = EMSwriteIndexMap(mmapID, key); + volatile int64_t *bufInt64 = (int64_t *) emsBuf; + volatile double *bufDouble = (double *) emsBuf; + char *bufChar = (char *) emsBuf; + EMStag_t oldTag; - if (info.Length() == 2) { - int64_t idx = EMSwriteIndexMap(info); - volatile int64_t *bufInt64 = (int64_t *) emsBuf; - volatile double *bufDouble = (double *) emsBuf; - char *bufChar = (char *) emsBuf; - EMStag_t oldTag; - - if (idx < 0 || idx >= bufInt64[EMScbData(EMS_ARR_NELEM)]) { - Nan::ThrowError("EMSfaa: index out of bounds"); - return; - } + if (idx < 0 || idx >= bufInt64[EMScbData(EMS_ARR_NELEM)]) { + fprintf(stderr, "EMSfaa: index out of bounds\n"); + return false; + } - { - volatile EMStag_t *maptag; - if (EMSisMapped) { maptag = &bufTags[EMSmapTag(idx)]; } - else { maptag = NULL; } - // Wait until the data is FULL, mark it busy while FAA is performed - oldTag.byte = EMStransitionFEtag(&bufTags[EMSdataTag(idx)], maptag, - EMS_TAG_FULL, EMS_TAG_BUSY, EMS_TAG_ANY); - } - oldTag.tags.fe = EMS_TAG_FULL; // When written back, mark FULL - int argType = EMSv8toEMStype(info[1], false); // Never add to an object, treat as string - switch (oldTag.tags.type) { - case EMS_TYPE_BOOLEAN: { // Bool + _______ - bool retBool = bufInt64[EMSdataData(idx)]; // Read original value in memory - switch (argType) { - case EMS_TYPE_INTEGER: // Bool + Int - bufInt64[EMSdataData(idx)] += info[1]->ToInteger()->Value(); - oldTag.tags.type = EMS_TYPE_INTEGER; - break; - case EMS_TYPE_FLOAT: // Bool + Float - bufDouble[EMSdataData(idx)] = - (double) bufInt64[EMSdataData(idx)] + info[1]->ToNumber()->Value(); - oldTag.tags.type = EMS_TYPE_FLOAT; - break; - case EMS_TYPE_UNDEFINED: // Bool + undefined - bufDouble[EMSdataData(idx)] = NAN; - oldTag.tags.type = EMS_TYPE_FLOAT; - break; - case EMS_TYPE_BOOLEAN: // Bool + Bool - bufInt64[EMSdataData(idx)] += info[1]->ToBoolean()->Value(); - oldTag.tags.type = EMS_TYPE_INTEGER; - break; - case EMS_TYPE_STRING: { // Bool + string - std::string argString(*Nan::Utf8String(info[1])); - const char *arg_c_str = argString.c_str(); - int64_t textOffset; - EMS_ALLOC(textOffset, argString.length() + 1 + 5, // String length + Terminating null + 'false' - "EMSfaa(bool+string): out of memory to store string", ); - sprintf(EMSheapPtr(textOffset), "%s%s", - bufInt64[EMSdataData(idx)] ? "true" : "false", arg_c_str); - bufInt64[EMSdataData(idx)] = textOffset; - oldTag.tags.type = EMS_TYPE_STRING; - } - break; - default: - Nan::ThrowError("EMSfaa: Data is BOOL, but FAA arg type is unknown"); - return; + volatile EMStag_t *maptag; + if (EMSisMapped) { maptag = &bufTags[EMSmapTag(idx)]; } + else { maptag = NULL; } + // Wait until the data is FULL, mark it busy while FAA is performed + oldTag.byte = EMStransitionFEtag(&bufTags[EMSdataTag(idx)], maptag, + EMS_TAG_FULL, EMS_TAG_BUSY, EMS_TAG_ANY); + oldTag.tags.fe = EMS_TAG_FULL; // When written back, mark FULL + switch (oldTag.tags.type) { + case EMS_TYPE_BOOLEAN: { // Bool + _______ + bool retBool = bufInt64[EMSdataData(idx)]; // Read original value in memory + returnValue->value = (void *) retBool; + returnValue->type = EMS_TYPE_BOOLEAN; + switch (value->type) { + case EMS_TYPE_INTEGER: // Bool + Int + bufInt64[EMSdataData(idx)] += (int64_t) value->value; + oldTag.tags.type = EMS_TYPE_INTEGER; + break; + case EMS_TYPE_FLOAT: { // Bool + Float + ulong_double alias; + alias.u64 = (uint64_t) value->value; + bufDouble[EMSdataData(idx)] = + (double) bufInt64[EMSdataData(idx)] + alias.d; + oldTag.tags.type = EMS_TYPE_FLOAT; } - // Write the new type and set the tag to Full, then return the original value - bufTags[EMSdataTag(idx)].byte = oldTag.byte; - if (EMSisMapped) bufTags[EMSmapTag(idx)].tags.fe = EMS_TAG_FULL; - info.GetReturnValue().Set(Nan::New(retBool)); - return; - } // End of: Bool + ___ + break; + case EMS_TYPE_UNDEFINED: // Bool + undefined + bufDouble[EMSdataData(idx)] = NAN; + oldTag.tags.type = EMS_TYPE_FLOAT; + break; + case EMS_TYPE_BOOLEAN: // Bool + Bool + bufInt64[EMSdataData(idx)] += (int64_t) value->value; + oldTag.tags.type = EMS_TYPE_INTEGER; + break; + case EMS_TYPE_STRING: { // Bool + string + int64_t textOffset; + EMS_ALLOC(textOffset, strlen((const char *) value->value) + 1 + 5, // String length + Terminating null + 'false' + bufChar, "EMSfaa(bool+string): out of memory to store string\n", false); + sprintf(EMSheapPtr(textOffset), "%s%s", + bufInt64[EMSdataData(idx)] ? "true" : "false", (const char *) value->value); + bufInt64[EMSdataData(idx)] = textOffset; + oldTag.tags.type = EMS_TYPE_STRING; + } + break; + default: + fprintf(stderr, "EMSfaa: Data is BOOL, but FAA arg type is unknown\n"); + return false; - case EMS_TYPE_INTEGER: { - int32_t retInt = bufInt64[EMSdataData(idx)]; // Read original value in memory - switch (argType) { - case EMS_TYPE_INTEGER: { // Int + int - int64_t memInt = bufInt64[EMSdataData(idx)]; - // TODO: Magic max int promotion to float - if (memInt >= (1 << 30)) { // Possible integer overflow, convert to float - bufDouble[EMSdataData(idx)] = - (double) bufInt64[EMSdataData(idx)] + (double) (info[1]->ToInteger()->Value()); - oldTag.tags.type = EMS_TYPE_FLOAT; - } else { // Did not overflow to flow, still an integer - bufInt64[EMSdataData(idx)] += info[1]->ToInteger()->Value(); - } - } - break; - case EMS_TYPE_FLOAT: // Int + float - bufDouble[EMSdataData(idx)] = - (double) bufInt64[EMSdataData(idx)] + info[1]->ToNumber()->Value(); - oldTag.tags.type = EMS_TYPE_FLOAT; - break; - case EMS_TYPE_UNDEFINED: // Int + undefined - bufDouble[EMSdataData(idx)] = NAN; + } + // Write the new type and set the tag to Full, then return the original value + bufTags[EMSdataTag(idx)].byte = oldTag.byte; + if (EMSisMapped) bufTags[EMSmapTag(idx)].tags.fe = EMS_TAG_FULL; + return true; + } // End of: Bool + ___ + + case EMS_TYPE_INTEGER: { + int32_t retInt = bufInt64[EMSdataData(idx)]; // Read original value in memory + returnValue->type = EMS_TYPE_INTEGER; + returnValue->value = (void *) (int64_t) retInt; + switch (value->type) { + case EMS_TYPE_INTEGER: { // Int + int + int64_t memInt = bufInt64[EMSdataData(idx)] + (int64_t) value->value; + // TODO: Magic max int promotion to float + if (memInt >= (1 << 30)) { // Possible integer overflow, convert to float + bufDouble[EMSdataData(idx)] = (double) memInt; oldTag.tags.type = EMS_TYPE_FLOAT; - break; - case EMS_TYPE_BOOLEAN: // Int + bool - bufInt64[EMSdataData(idx)] += info[1]->ToBoolean()->Value(); - break; - case EMS_TYPE_STRING: { // int + string - std::string argString(*Nan::Utf8String(info[1])); - const char *arg_c_str = argString.c_str(); - int64_t textOffset; - EMS_ALLOC(textOffset, argString.length() + 1 + MAX_NUMBER2STR_LEN, - "EMSfaa(int+string): out of memory to store string", ); - sprintf(EMSheapPtr(textOffset), "%lld%s", - (long long int) bufInt64[EMSdataData(idx)], arg_c_str); - bufInt64[EMSdataData(idx)] = textOffset; - oldTag.tags.type = EMS_TYPE_STRING; + } else { // Did not overflow to flow, still an integer + bufInt64[EMSdataData(idx)] = memInt; } - break; - default: - Nan::ThrowError("EMSfaa: Data is INT, but FAA arg type is unknown"); - return; } - // Write the new type and set the tag to Full, then return the original value - bufTags[EMSdataTag(idx)].byte = oldTag.byte; - if (EMSisMapped) bufTags[EMSmapTag(idx)].tags.fe = EMS_TAG_FULL; - info.GetReturnValue().Set(Nan::New(retInt)); - return; - } // End of: Integer + ____ + break; + case EMS_TYPE_FLOAT: { // Int + float + ulong_double alias; + alias.u64 = (uint64_t) value->value; + bufDouble[EMSdataData(idx)] = + (double) bufInt64[EMSdataData(idx)] + alias.d; + oldTag.tags.type = EMS_TYPE_FLOAT; + } + break; + case EMS_TYPE_UNDEFINED: // Int + undefined + bufDouble[EMSdataData(idx)] = NAN; + oldTag.tags.type = EMS_TYPE_FLOAT; + break; + case EMS_TYPE_BOOLEAN: // Int + bool + bufInt64[EMSdataData(idx)] += (int64_t) value->value; + break; + case EMS_TYPE_STRING: { // int + string + int64_t textOffset; + EMS_ALLOC(textOffset, strlen((const char *) value->value) + 1 + MAX_NUMBER2STR_LEN, + bufChar, "EMSfaa(int+string): out of memory to store string\n", false); + sprintf(EMSheapPtr(textOffset), "%lld%s", + (long long int) bufInt64[EMSdataData(idx)], (const char *) value->value); + bufInt64[EMSdataData(idx)] = textOffset; + oldTag.tags.type = EMS_TYPE_STRING; + } + break; + default: + fprintf(stderr, "EMSfaa: Data is INT, but FAA arg type is unknown\n"); + return false; + } + // Write the new type and set the tag to Full, then return the original value + bufTags[EMSdataTag(idx)].byte = oldTag.byte; + if (EMSisMapped) bufTags[EMSmapTag(idx)].tags.fe = EMS_TAG_FULL; + return true; + } // End of: Integer + ____ - case EMS_TYPE_FLOAT: { - double retDbl = bufDouble[EMSdataData(idx)]; - switch (argType) { - case EMS_TYPE_INTEGER: // Float + int - bufDouble[EMSdataData(idx)] += (double) info[1]->ToInteger()->Value(); - break; - case EMS_TYPE_FLOAT: // Float + float - bufDouble[EMSdataData(idx)] += info[1]->ToNumber()->Value(); - break; - case EMS_TYPE_BOOLEAN: // Float + boolean - bufDouble[EMSdataData(idx)] += (double) info[1]->ToInteger()->Value(); - break; - case EMS_TYPE_STRING: { // Float + string - std::string argString(*Nan::Utf8String(info[1])); - const char *arg_c_str = argString.c_str(); - int64_t textOffset; - EMS_ALLOC(textOffset, argString.length() + 1 + MAX_NUMBER2STR_LEN, - "EMSfaa(float+string): out of memory to store string", ); - sprintf(EMSheapPtr(textOffset), "%lf%s", bufDouble[EMSdataData(idx)], arg_c_str); - bufInt64[EMSdataData(idx)] = textOffset; - oldTag.tags.type = EMS_TYPE_STRING; - } - break; - case EMS_TYPE_UNDEFINED: // Float + Undefined - bufDouble[EMSdataData(idx)] = NAN; - break; - default: - Nan::ThrowError("EMSfaa: Data is FLOAT, but arg type unknown"); - return; + case EMS_TYPE_FLOAT: { + double retDbl = bufDouble[EMSdataData(idx)]; + returnValue->type = EMS_TYPE_FLOAT; + ulong_double alias; + alias.d = retDbl; + returnValue->value = (void *) alias.u64; + + switch (value->type) { + case EMS_TYPE_INTEGER: // Float + int + bufDouble[EMSdataData(idx)] += (double) ((int64_t) value->value); + break; + case EMS_TYPE_FLOAT: { // Float + float + ulong_double alias; + alias.u64 = (uint64_t) value->value; + bufDouble[EMSdataData(idx)] += alias.d; + } + break; + case EMS_TYPE_BOOLEAN: // Float + boolean + bufDouble[EMSdataData(idx)] += (double) ((int64_t) value->value); + break; + case EMS_TYPE_STRING: { // Float + string + int64_t textOffset; + EMS_ALLOC(textOffset, strlen((const char *) value->value) + 1 + MAX_NUMBER2STR_LEN, + bufChar, "EMSfaa(float+string): out of memory to store string\n", false); + sprintf(EMSheapPtr(textOffset), "%lf%s", bufDouble[EMSdataData(idx)], (const char *) value->value); + bufInt64[EMSdataData(idx)] = textOffset; + oldTag.tags.type = EMS_TYPE_STRING; } - // Write the new type and set the tag to Full, then return the original value - bufTags[EMSdataTag(idx)].byte = oldTag.byte; - if (EMSisMapped) bufTags[EMSmapTag(idx)].tags.fe = EMS_TAG_FULL; - info.GetReturnValue().Set(Nan::New(retDbl)); - return; - } // End of: float + _______ + break; + case EMS_TYPE_UNDEFINED: // Float + Undefined + bufDouble[EMSdataData(idx)] = NAN; + break; + default: + fprintf(stderr, "EMSfaa: Data is FLOAT, but arg type unknown\n"); + return false; + } + // Write the new type and set the tag to Full, then return the original value + bufTags[EMSdataTag(idx)].byte = oldTag.byte; + if (EMSisMapped) bufTags[EMSmapTag(idx)].tags.fe = EMS_TAG_FULL; + return true; + } // End of: float + _______ - case EMS_TYPE_STRING: { - info.GetReturnValue().Set(Nan::New(EMSheapPtr(bufInt64[EMSdataData(idx)])).ToLocalChecked()); - int64_t textOffset; - int64_t len; - switch (argType) { - case EMS_TYPE_INTEGER: // string + int - len = strlen(EMSheapPtr(bufInt64[EMSdataData(idx)])) + 1 + MAX_NUMBER2STR_LEN; - EMS_ALLOC(textOffset, len, "EMSfaa(string+int): out of memory to store string", ); - sprintf(EMSheapPtr(textOffset), "%s%lld", - EMSheapPtr(bufInt64[EMSdataData(idx)]), - (long long int) info[1]->ToInteger()->Value()); - break; - case EMS_TYPE_FLOAT: // string + dbl - len = strlen(EMSheapPtr(bufInt64[EMSdataData(idx)])) + 1 + MAX_NUMBER2STR_LEN; - EMS_ALLOC(textOffset, len, "EMSfaa(string+dbl): out of memory to store string", ); - sprintf(EMSheapPtr(textOffset), "%s%lf", - EMSheapPtr(bufInt64[EMSdataData(idx)]), - info[1]->ToNumber()->Value()); - break; - case EMS_TYPE_STRING: { // string + string - std::string argString(*Nan::Utf8String(info[1])); - const char *arg_c_str = argString.c_str(); - len = strlen(EMSheapPtr(bufInt64[EMSdataData(idx)])) + 1 + argString.length(); - EMS_ALLOC(textOffset, len, "EMSfaa(string+string): out of memory to store string", ); - sprintf(EMSheapPtr(textOffset), "%s%s", - EMSheapPtr(bufInt64[EMSdataData(idx)]), - arg_c_str); - } - break; - case EMS_TYPE_BOOLEAN: // string + bool - static char strTrue[] = "true"; - static char strFalse[] = "false"; - char *tfString; - if (info[1]->ToBoolean()->Value()) tfString = strTrue; - else tfString = strFalse; - len = strlen(EMSheapPtr(bufInt64[EMSdataData(idx)])) + 1 + strlen(tfString); - EMS_ALLOC(textOffset, len, "EMSfaa(string+bool): out of memory to store string", ); - sprintf(EMSheapPtr(textOffset), "%s%s", - EMSheapPtr(bufInt64[EMSdataData(idx)]), - tfString); - break; - case EMS_TYPE_UNDEFINED: // string + undefined - len = strlen(EMSheapPtr(bufInt64[EMSdataData(idx)])) + 1 + strlen("undefined"); - EMS_ALLOC(textOffset, len, "EMSfaa(string+bool): out of memory to store string", ); - sprintf(EMSheapPtr(textOffset), "%s%s", - EMSheapPtr(bufInt64[EMSdataData(idx)]), - "undefined"); - break; - default: - Nan::ThrowError("EMSfaa(string+?): Unknown data type"); - return; + case EMS_TYPE_STRING: { + // size_t oldStrLen = (size_t) emsMem_size(EMS_MEM_MALLOCBOT(bufChar), bufInt64[EMSdataData(idx)]); + size_t oldStrLen = strlen(EMSheapPtr(bufInt64[EMSdataData(idx)])); + returnValue->type = EMS_TYPE_STRING; + returnValue->value = malloc(oldStrLen + 1); // freed in NodeJSfaa + if(returnValue->value == NULL) { + fprintf(stderr, "EMSfaa: Unable to malloc temporary old string\n"); + return false; + } + strcpy((char *) returnValue->value, EMSheapPtr(bufInt64[EMSdataData(idx)])); + int64_t textOffset; + size_t len; + switch (value->type) { + case EMS_TYPE_INTEGER: // string + int + len = oldStrLen + 1 + MAX_NUMBER2STR_LEN; + EMS_ALLOC(textOffset, len, bufChar, "EMSfaa(string+int): out of memory to store string\n", false); + sprintf(EMSheapPtr(textOffset), "%s%lld", + EMSheapPtr(bufInt64[EMSdataData(idx)]), + (long long int) value->value); + break; + case EMS_TYPE_FLOAT: { // string + dbl + ulong_double alias; + alias.u64 = (uint64_t) value->value; + len = oldStrLen + 1 + MAX_NUMBER2STR_LEN; + EMS_ALLOC(textOffset, len, bufChar, "EMSfaa(string+dbl): out of memory to store string\n", false); + sprintf(EMSheapPtr(textOffset), "%s%lf", + EMSheapPtr(bufInt64[EMSdataData(idx)]), alias.d); } - EMS_FREE(bufInt64[EMSdataData(idx)]); - bufInt64[EMSdataData(idx)] = textOffset; - oldTag.tags.type = EMS_TYPE_STRING; - // Write the new type and set the tag to Full, then return the original value - bufTags[EMSdataTag(idx)].byte = oldTag.byte; - if (EMSisMapped) bufTags[EMSmapTag(idx)].tags.fe = EMS_TAG_FULL; - // return value was set at the top of this block - return; - } // End of: String + __________ + break; + case EMS_TYPE_STRING: { // string + string + len = oldStrLen + 1 + strlen((const char *) value->value); + EMS_ALLOC(textOffset, len, bufChar, "EMSfaa(string+string): out of memory to store string\n", false); + sprintf(EMSheapPtr(textOffset), "%s%s", + EMSheapPtr(bufInt64[EMSdataData(idx)]), (const char *) value->value); + } + break; + case EMS_TYPE_BOOLEAN: // string + bool + len = strlen(EMSheapPtr(bufInt64[EMSdataData(idx)])) + 1 + 5; // 5==strlen("false") + EMS_ALLOC(textOffset, len, bufChar, "EMSfaa(string+bool): out of memory to store string\n", false); + sprintf(EMSheapPtr(textOffset), "%s%s", + EMSheapPtr(bufInt64[EMSdataData(idx)]), (bool) value->value ? "true" : "false"); + break; + case EMS_TYPE_UNDEFINED: // string + undefined + len = strlen(EMSheapPtr(bufInt64[EMSdataData(idx)])) + 1 + strlen("undefined"); + EMS_ALLOC(textOffset, len, bufChar, "EMSfaa(string+undefined): out of memory to store string\n", false); + sprintf(EMSheapPtr(textOffset), "%s%s", + EMSheapPtr(bufInt64[EMSdataData(idx)]), "undefined"); + break; + default: + fprintf(stderr, "EMSfaa(string+?): Unknown data type\n"); + return false; + } + EMS_FREE(bufInt64[EMSdataData(idx)]); + bufInt64[EMSdataData(idx)] = textOffset; + oldTag.tags.type = EMS_TYPE_STRING; + // Write the new type and set the tag to Full, then return the original value + bufTags[EMSdataTag(idx)].byte = oldTag.byte; + if (EMSisMapped) bufTags[EMSmapTag(idx)].tags.fe = EMS_TAG_FULL; + // return value was set at the top of this block + return true; + } // End of: String + __________ - case EMS_TYPE_UNDEFINED: { - switch (argType) { // Undefined + Int, dloat, bool, or undef - case EMS_TYPE_INTEGER: - case EMS_TYPE_FLOAT: - case EMS_TYPE_BOOLEAN: - case EMS_TYPE_UNDEFINED: - bufDouble[EMSdataData(idx)] = NAN; - oldTag.tags.type = EMS_TYPE_FLOAT; - break; - case EMS_TYPE_STRING: { // Undefined + string - std::string argString(*Nan::Utf8String(info[1])); - const char *arg_c_str = argString.c_str(); - int64_t textOffset; - EMS_ALLOC(textOffset, argString.length() + 1 + 3, // 3 = strlen("NaN"); - "EMSfaa(undef+String): out of memory to store string", ); - sprintf(EMSheapPtr(textOffset), "NaN%s", arg_c_str); - bufInt64[EMSdataData(idx)] = textOffset; - oldTag.tags.type = EMS_TYPE_UNDEFINED; - } - break; - default: - Nan::ThrowError("EMSfaa(Undefined+___: Unknown stored data type"); - return; + case EMS_TYPE_UNDEFINED: { + returnValue->type = EMS_TYPE_UNDEFINED; + returnValue->value = (void *) 0xf00dd00f; + switch (value->type) { // Undefined + Int, dloat, bool, or undef + case EMS_TYPE_INTEGER: + case EMS_TYPE_FLOAT: + case EMS_TYPE_BOOLEAN: + case EMS_TYPE_UNDEFINED: + bufDouble[EMSdataData(idx)] = NAN; + oldTag.tags.type = EMS_TYPE_FLOAT; + break; + case EMS_TYPE_STRING: { // Undefined + string + int64_t textOffset; + EMS_ALLOC(textOffset, strlen((const char *) value->value) + 1 + 3, // 3 = strlen("NaN"); + bufChar, "EMSfaa(undef+String): out of memory to store string\n", false); + sprintf(EMSheapPtr(textOffset), "NaN%s", (const char *) value->value); + bufInt64[EMSdataData(idx)] = textOffset; + oldTag.tags.type = EMS_TYPE_UNDEFINED; } - // Write the new type and set the tag to Full, then return the original value - bufTags[EMSdataTag(idx)].byte = oldTag.byte; - if (EMSisMapped) bufTags[EMSmapTag(idx)].tags.fe = EMS_TAG_FULL; - info.GetReturnValue().Set(Nan::Undefined()); - return; + break; + default: + fprintf(stderr, "EMSfaa(Undefined+___: Unknown stored data type\n"); + return false; } - default: - Nan::ThrowError("EMSfaa(?+___: Unknown stored data type"); - return; + // Write the new type and set the tag to Full, then return the original value + bufTags[EMSdataTag(idx)].byte = oldTag.byte; + if (EMSisMapped) bufTags[EMSmapTag(idx)].tags.fe = EMS_TAG_FULL; + return true; } + default: + fprintf(stderr, "EMSfaa(?+___: Unknown stored data type\n"); + return false; } - Nan::ThrowError("EMSfaa: Wrong number of arguments"); - return; + fprintf(stderr, "EMSfaa: Unknown everything -- failing for unknown reasons\n"); + return false; } //================================================================== // Atomic Compare and Swap -// -void EMScas(const Nan::FunctionCallbackInfo &info) { - THIS_INFO_TO_EMSBUF(info, "mmapID"); +bool EMScas(int mmapID, EMSvalueType *key, + EMSvalueType *oldValue, EMSvalueType *newValue, + EMSvalueType *returnValue) { + void *emsBuf = emsBufs[mmapID]; + volatile int64_t *bufInt64 = (int64_t *) emsBuf; + int64_t idx = EMSkey2index(emsBuf, key, EMSisMapped); + char * bufChar = (char *) emsBuf; + volatile EMStag_t *bufTags = (EMStag_t *) emsBuf; + EMStag_t newTag; + int64_t textOffset; + int swapped = false; - if (info.Length() >= 3) { - int64_t idx = EMSreadIndexMap(info); - volatile int64_t *bufInt64 = (int64_t *) emsBuf; - volatile double *bufDouble = (double *) emsBuf; - char * bufChar = (char *) emsBuf; - volatile EMStag_t *bufTags = (EMStag_t *) emsBuf; - EMStag_t newTag; - bool boolMemVal = false; - int32_t intMemVal = -1; - double floatMemVal = 0.0; - int64_t textOffset; - std::string oldString; - char stringMemVal[MAX_KEY_LEN]; - int swapped = false; + if ((!EMSisMapped && idx < 0) || idx >= bufInt64[EMScbData(EMS_ARR_NELEM)]) { + fprintf(stderr, "EMScas: index out of bounds\n"); + return false; + } - if ((!EMSisMapped && idx < 0) || idx >= bufInt64[EMScbData(EMS_ARR_NELEM)]) { - Nan::ThrowError("EMScas: index out of bounds"); - return; - } + size_t memStrLen; + unsigned char memType; +retry_on_undefined: + if(EMSisMapped && idx < 0) { + memType = EMS_TYPE_UNDEFINED; + } else { + // Wait for the memory to be Full, then mark it Busy while CAS works + volatile EMStag_t *maptag; + if (EMSisMapped) { maptag = &bufTags[EMSmapTag(idx)]; } + else { maptag = NULL; } + // Wait until the data is FULL, mark it busy while FAA is performed + EMStransitionFEtag(&bufTags[EMSdataTag(idx)], maptag, + EMS_TAG_FULL, EMS_TAG_BUSY, EMS_TAG_ANY); + memType = bufTags[EMSdataTag(idx)].tags.type; + } - int oldType = EMSv8toEMStype(info[1], false); // Never CAS an object, treat as string - int newType; - if (info.Length() == 4) { - newType = EMSv8toEMStype(info[2], info[3]->ToBoolean()->Value()); - } else { - newType = EMSv8toEMStype(info[2], false); - } + // Read the value in memory + returnValue->type = memType; + switch (memType) { + case EMS_TYPE_UNDEFINED: + returnValue->value = (void *) 0xf00dcafe; + break; + case EMS_TYPE_BOOLEAN: + case EMS_TYPE_INTEGER: + case EMS_TYPE_FLOAT: + returnValue->value = (void *) bufInt64[EMSdataData(idx)]; + break; + case EMS_TYPE_JSON: + case EMS_TYPE_STRING: + memStrLen = strlen(EMSheapPtr(bufInt64[EMSdataData(idx)])); + returnValue->value = malloc(memStrLen + 1); // freed in NodeJSfaa + if(returnValue->value == NULL) { + fprintf(stderr, "EMScas: Unable to allocate space to return old string\n"); + return false; + } + strcpy((char *) returnValue->value, EMSheapPtr(bufInt64[EMSdataData(idx)])); + break; + default: + fprintf(stderr, "EMScas: memType not recognized\n"); + return false; + } - int memType; - retry_on_undefined: + // Compare the value in memory the the "old" CAS value + if (oldValue->type == memType) { + // Allocate on Write: If this memory was undefined (ie: unallocated), + // allocate the index map, store the undefined, and start over again. if(EMSisMapped && idx < 0) { - memType = EMS_TYPE_UNDEFINED; - } else { - // Wait for the memory to be Full, then mark it Busy while CAS works - volatile EMStag_t *maptag; - if (EMSisMapped) { maptag = &bufTags[EMSmapTag(idx)]; } - else { maptag = NULL; } - // Wait until the data is FULL, mark it busy while FAA is performed - EMStransitionFEtag(&bufTags[EMSdataTag(idx)], maptag, - EMS_TAG_FULL, EMS_TAG_BUSY, EMS_TAG_ANY); - memType = bufTags[EMSdataTag(idx)].tags.type; + idx = EMSwriteIndexMap(mmapID, key); + if (idx < 0) { + fprintf(stderr, "EMScas: Not able to allocate map on CAS of undefined data\n"); + return false; + } + bufInt64[EMSdataData(idx)] = 0xcafebabe; + newTag.tags.fe = EMS_TAG_FULL; + newTag.tags.rw = 0; + newTag.tags.type = EMS_TYPE_UNDEFINED; + bufTags[EMSdataTag(idx)].byte = newTag.byte; + goto retry_on_undefined; } - - // Read the value in memory switch (memType) { case EMS_TYPE_UNDEFINED: + swapped = true; break; case EMS_TYPE_BOOLEAN: - boolMemVal = bufInt64[EMSdataData(idx)]; - break; case EMS_TYPE_INTEGER: - intMemVal = bufInt64[EMSdataData(idx)]; - break; case EMS_TYPE_FLOAT: - floatMemVal = bufDouble[EMSdataData(idx)]; + if (returnValue->value == oldValue->value) + swapped = true; break; case EMS_TYPE_JSON: case EMS_TYPE_STRING: - strncpy(stringMemVal, EMSheapPtr(bufInt64[EMSdataData(idx)]), MAX_KEY_LEN); - break; - default: - Nan::ThrowError("EMScas: memType not recognized"); - return; - } - - // Compare the value in memory the the "old" CAS value - if (oldType == memType) { - // Allocate on Write: If this memory was undefined (ie: unallocated), - // allocate the index map, store the undefined, and start over again. - if(EMSisMapped && idx < 0) { - idx = EMSwriteIndexMap(info); - if (idx < 0) { - Nan::ThrowError("EMScas: Not able to allocate map on CAS of undefined data"); - return; - } - bufInt64[EMSdataData(idx)] = 0xcafebabe; - newTag.tags.fe = EMS_TAG_FULL; - newTag.tags.rw = 0; - newTag.tags.type = EMS_TYPE_UNDEFINED; - bufTags[EMSdataTag(idx)].byte = newTag.byte; - goto retry_on_undefined; - } - switch (memType) { - case EMS_TYPE_UNDEFINED: + if (strcmp((const char *) returnValue->value, (const char *) oldValue->value) == 0) { swapped = true; - break; - case EMS_TYPE_BOOLEAN: - if (boolMemVal == info[1]->ToBoolean()->Value()) swapped = true; - break; - case EMS_TYPE_INTEGER: - if (intMemVal == info[1]->ToInteger()->Value()) swapped = true; - break; - case EMS_TYPE_FLOAT: - if (floatMemVal == info[1]->ToNumber()->Value()) swapped = true; - break; - case EMS_TYPE_JSON: - case EMS_TYPE_STRING: - oldString = std::string(*Nan::Utf8String(info[1])); - if (strncmp(stringMemVal, oldString.c_str(), MAX_KEY_LEN) == 0) { - swapped = true; - } - break; - default: - Nan::ThrowError("EMScas: oldTag not recognized"); - return; - } - } - - // If memory==old then write the new value - newTag.tags.fe = EMS_TAG_FULL; - newTag.tags.rw = 0; - newTag.tags.type = memType; - if (swapped) { - newTag.tags.type = newType; - switch (newType) { - case EMS_TYPE_UNDEFINED: - bufInt64[EMSdataData(idx)] = 0xdeadbeef; - break; - case EMS_TYPE_BOOLEAN: - bufInt64[EMSdataData(idx)] = (int64_t) info[2]->ToBoolean()->Value(); - break; - case EMS_TYPE_INTEGER: - bufInt64[EMSdataData(idx)] = info[2]->ToInteger()->Value(); - break; - case EMS_TYPE_FLOAT: - bufDouble[EMSdataData(idx)] = info[2]->ToNumber()->Value(); - break; - case EMS_TYPE_JSON: - case EMS_TYPE_STRING: { - if (memType == EMS_TYPE_STRING) EMS_FREE(bufInt64[EMSdataData(idx)]); - std::string newString(*Nan::Utf8String(info[2])); - const char *stringNewVal = newString.c_str(); - EMS_ALLOC(textOffset, newString.length() + 1, "EMScas(string): out of memory to store string", ); - strcpy(EMSheapPtr(textOffset), stringNewVal); - bufInt64[EMSdataData(idx)] = textOffset; } - break; - default: - Nan::ThrowError("EMScas(): Unrecognized new type"); - return; - } + break; + default: + fprintf(stderr, "EMScas: oldTag not recognized"); + return false; } + } - // Set the tag back to Full and return the original value - bufTags[EMSdataTag(idx)].byte = newTag.byte; - // If there is a map, set the map's tag back to full - if (EMSisMapped) bufTags[EMSmapTag(idx)].tags.fe = EMS_TAG_FULL; - switch (memType) { + // If memory==old then write the new value + newTag.tags.fe = EMS_TAG_FULL; + newTag.tags.rw = 0; + newTag.tags.type = memType; + if (swapped) { + if (memType == EMS_TYPE_STRING || memType == EMS_TYPE_JSON) + EMS_FREE((size_t) bufInt64[EMSdataData(idx)]); + newTag.tags.type = newValue->type; + switch (newValue->type) { case EMS_TYPE_UNDEFINED: - info.GetReturnValue().Set(Nan::Undefined()); - return; + bufInt64[EMSdataData(idx)] = 0xbeeff00d; + break; case EMS_TYPE_BOOLEAN: - info.GetReturnValue().Set(Nan::New(boolMemVal)); - return; case EMS_TYPE_INTEGER: - info.GetReturnValue().Set(Nan::New(intMemVal)); - return; case EMS_TYPE_FLOAT: - info.GetReturnValue().Set(Nan::New(floatMemVal)); - return; + bufInt64[EMSdataData(idx)] = (int64_t) newValue->value; + break; case EMS_TYPE_JSON: case EMS_TYPE_STRING: - info.GetReturnValue().Set(Nan::New(stringMemVal).ToLocalChecked()); - return; + EMS_ALLOC(textOffset, strlen((const char *) newValue->value) + 1, + bufChar, "EMScas(string): out of memory to store string\n", false); + strcpy(EMSheapPtr(textOffset), (const char *) newValue->value); + bufInt64[EMSdataData(idx)] = textOffset; + break; default: - Nan::ThrowError("EMScas(): Unrecognized mem type"); - return; + fprintf(stderr, "EMScas(): Unrecognized new type\n"); + return false; } - } else { - Nan::ThrowError("EMS_CASnumber wrong number of arguments"); - return; } -} + // Set the tag back to Full and return the original value + bufTags[EMSdataTag(idx)].byte = newTag.byte; + // If there is a map, set the map's tag back to full + if (EMSisMapped) + bufTags[EMSmapTag(idx)].tags.fe = EMS_TAG_FULL; + return true; +}