Skip to content

Commit

Permalink
Add a JSON service for 1D transformations
Browse files Browse the repository at this point in the history
  • Loading branch information
xaionaro committed Nov 9, 2021
1 parent 1f8155d commit 3bade79
Show file tree
Hide file tree
Showing 9 changed files with 399 additions and 2 deletions.
Binary file added .Makefile.swp
Binary file not shown.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
*.i*86
*.x86_64
*.hex
/demo
/service
/service_backend

# Debug files
*.dSYM/
Expand Down
12 changes: 10 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
NVCC=nvcc
CFLAGS="-arch=sm_30"
#CFLAGS="-arch=sm_30"
LDFLAGS=-lcublas

PDWTCORE=src/wt.cu src/common.cu src/utils.cu src/separable.cu src/nonseparable.cu src/haar.cu src/filters.cpp
Expand All @@ -25,6 +25,14 @@ demo: $(PDWTCORE) src/demo.cpp src/io.cpp
$(NVCC) -g $(CFLAGS) -odir build -c $^
$(NVCC) $(CFLAGS) -o demo $(PDWTOBJ) build/demo.o build/io.o $(LDFLAGS)

service_backend: $(PDWTCORE) src/service_backend.cpp
mkdir -p build
$(NVCC) -g $(CFLAGS) -odir build -c $^
$(NVCC) $(CFLAGS) -o service_backend $(PDWTOBJ) build/service_backend.o $(LDFLAGS)


service: service_backend
cd src/service && go build -o ../../service ./

libpdwt.so: $(PDWTCORE)
mkdir -p build
Expand All @@ -41,4 +49,4 @@ libpdwtd.so: $(PDWTCORE)


clean:
rm -rf build demo libpdwt*.so
rm -rf build demo libpdwt*.so service service_backend
179 changes: 179 additions & 0 deletions src/service/backend.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
package main

import "C"
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"reflect"
"strconv"
"sync"
"unsafe"

"github.com/pierrepaleo/PDWT/src/service/types"
)

type backend struct {
sync.Mutex
*exec.Cmd
BackendExecutablePath string
Stdin io.WriteCloser
Stdout io.ReadCloser
Stderr io.ReadCloser
}

func getBackend(backendExecutablePath string) (*backend, error) {
b := &backend{
BackendExecutablePath: backendExecutablePath,
}
if err := b.start(); err != nil {
return nil, fmt.Errorf("unable to start: %w", err)
}
return b, nil
}

func (b *backend) start() error {
cmd := exec.Command(b.BackendExecutablePath)
stdin, err := cmd.StdinPipe()
if err != nil {
return fmt.Errorf("unable to get stdin pipe: %w", err)
}
stdout, err := cmd.StdoutPipe()
if err != nil {
return fmt.Errorf("unable to get stdin pipe: %w", err)
}
stderr, err := cmd.StderrPipe()
if err != nil {
return fmt.Errorf("unable to get stderr pipe: %w", err)
}

err = cmd.Start()
if err != nil {
return fmt.Errorf("unable to start a backend: %w", err)
}

b.Cmd = cmd
b.Stdin = stdin
b.Stdout = stdout
b.Stderr = stderr
return nil
}

func (b *backend) Close() {
b.Mutex.Lock()
defer b.Mutex.Unlock()

b.close()
}

func (b *backend) close() {
b.Stdin.Close()
b.Stdout.Close()
b.Process.Kill()
}

func (b *backend) restart() {
b.close()
b.start()
}

func (b *backend) Execute(req *types.Request) (*types.Response, error) {
tmpDir := os.TempDir()

requestFile, err := ioutil.TempFile(tmpDir, ".PDWT-request-")
if err != nil {
return nil, fmt.Errorf("unable to create a request file: %w")
}

responseFile, err := ioutil.TempFile(tmpDir, ".PDWT-response-")
if err != nil {
return nil, fmt.Errorf("unable to create a response file: %w")
}

b.Lock()
defer b.Unlock()

valuesSlice := (*reflect.SliceHeader)(unsafe.Pointer(&req.Values))
valuesSlice.Cap *= int(unsafe.Sizeof(float32(0)))
valuesSlice.Len *= int(unsafe.Sizeof(float32(0)))
n, err := requestFile.Write(*(*[]byte)((unsafe.Pointer)(valuesSlice)))
if err != nil {
return nil, fmt.Errorf("unable to write data to the request file: %w", err)
}
if n != valuesSlice.Len {
return nil, fmt.Errorf("wrote invalid length of data to the request file: %d != %d", n, valuesSlice.Len)
}

const (
fieldSeparator = '\000'
requestTerminator = '\n'
)

var buf bytes.Buffer
_, err = buf.WriteString(req.Wavelet)
assertNoError(err)
err = buf.WriteByte(fieldSeparator)
assertNoError(err)
_, err = buf.WriteString(strconv.FormatUint(uint64(req.Levels), 10))
assertNoError(err)
err = buf.WriteByte(fieldSeparator)
assertNoError(err)
_, err = buf.WriteString(strconv.FormatBool(req.IsInverse))
assertNoError(err)
err = buf.WriteByte(fieldSeparator)
assertNoError(err)
_, err = buf.WriteString(requestFile.Name())
assertNoError(err)
err = buf.WriteByte(fieldSeparator)
assertNoError(err)
_, err = buf.WriteString(responseFile.Name())
err = buf.WriteByte(requestTerminator)
assertNoError(err)

_, err = b.Stdin.Write(buf.Bytes())
if err != nil {
b.restart()
return nil, fmt.Errorf("unable to write request '%s' to the backend's stdin: %w", buf.String(), err)
}

var outcome [256]byte
n, err = b.Stdout.Read(outcome[:])
if err != nil {
var errorDescriptionBuf [1024]byte
var errorDescription []byte
n, _ := b.Stderr.Read(errorDescriptionBuf[:])
if n > 0 {
errorDescription = errorDescriptionBuf[:n]
}
b.restart()
return nil, fmt.Errorf("unable to read the outcome from the backend's stdout: %w, stderr: %s", err, errorDescription)
}

if outcome[0] != '\n' {
b.restart()
return nil, fmt.Errorf("unsupported backend protocol, received: %X:%s", outcome[:n], outcome[:n])
}

responseRawData, err := ioutil.ReadAll(responseFile)
if err != nil {
b.restart()
return nil, fmt.Errorf("read raw data from the response file: %w", err)
}

if len(responseRawData)%int(unsafe.Sizeof(float32(0))) != 0 {
b.restart()
return nil, fmt.Errorf("unexpected size: %d %% 4 != 0", len(responseRawData))
}

responseSlice := (*reflect.SliceHeader)(unsafe.Pointer(&responseRawData))
responseSlice.Len /= int(unsafe.Sizeof(float32(0)))
responseSlice.Cap /= int(unsafe.Sizeof(float32(0)))
responseData := *(*[]float32)(unsafe.Pointer(responseSlice))

return &types.Response{
Values: responseData,
}, nil
}
3 changes: 3 additions & 0 deletions src/service/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/pierrepaleo/PDWT/src/service

go 1.13
65 changes: 65 additions & 0 deletions src/service/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package main

import (
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"net/http"

"github.com/pierrepaleo/PDWT/src/service/types"
)

func assertNoError(err error) {
if err != nil {
panic(err)
}
}

func main() {
backendPathFlag := flag.String("backend-path", "./service_backend", "")
listenAddrFlag := flag.String("listen-addr", ":13407", "")
flag.Parse()

backend, err := getBackend(*backendPathFlag)
assertNoError(err)

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
w.WriteHeader(400)
w.Write([]byte(fmt.Sprintf("unable to read body: %v", err)))
return
}

var request types.Request
err = json.Unmarshal(body, &request)
if err != nil {
w.WriteHeader(400)
w.Write([]byte(fmt.Sprintf("unable to un-JSON-ize: %v", err)))
return
}

response, err := backend.Execute(&request)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(fmt.Sprintf("unable to process the request: %v", err)))
return
}

responseJSON, err := json.Marshal(response)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(fmt.Sprintf("unable to JSON-ize the response: %v", err)))
return
}

w.Header().Set("Content-Type", "application/json")
w.Write(responseJSON)
})

log.Printf("listening for JSON requests at http://%s", *listenAddrFlag)
err = http.ListenAndServe(*listenAddrFlag, nil)
assertNoError(err)
}
8 changes: 8 additions & 0 deletions src/service/types/request.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package types

type Request struct {
Wavelet string
Levels uint
IsInverse bool
Values []float32
}
5 changes: 5 additions & 0 deletions src/service/types/response.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package types

type Response struct {
Values []float32
}
Loading

0 comments on commit 3bade79

Please sign in to comment.