github.com/eth-easl/loader@v0.0.0-20230908084258-8a37e1d94279/server/timed/timed.go (about)

     1  /*
     2   * MIT License
     3   *
     4   * Copyright (c) 2023 EASL and the vHive community
     5   *
     6   * Permission is hereby granted, free of charge, to any person obtaining a copy
     7   * of this software and associated documentation files (the "Software"), to deal
     8   * in the Software without restriction, including without limitation the rights
     9   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    10   * copies of the Software, and to permit persons to whom the Software is
    11   * furnished to do so, subject to the following conditions:
    12   *
    13   * The above copyright notice and this permission notice shall be included in all
    14   * copies or substantial portions of the Software.
    15   *
    16   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    17   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    18   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    19   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    20   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    21   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    22   * SOFTWARE.
    23   */
    24  
    25  package main
    26  
    27  import (
    28  	"context"
    29  	"errors"
    30  	"fmt"
    31  	util "github.com/eth-easl/loader/pkg/common"
    32  	"github.com/eth-easl/loader/pkg/workload/proto"
    33  	"net"
    34  	"os"
    35  	"strconv"
    36  	"time"
    37  
    38  	log "github.com/sirupsen/logrus"
    39  	"golang.org/x/sys/unix"
    40  	"google.golang.org/grpc"
    41  	"google.golang.org/grpc/reflection"
    42  )
    43  
    44  //! We don't enforce this limit anymore because no limits have set for the containers themselves
    45  //! (i.e., they are busrtable workloads controlled by K8s and won't get OOM-killed by the kernel).
    46  // const containerMemoryLimitMib = 512 // Default limit of k8s.
    47  
    48  type funcServer struct {
    49  	proto.UnimplementedExecutorServer
    50  }
    51  
    52  func busySpin(timeoutSem <-chan time.Time) {
    53  	/** `for { }` generates the assembly `jmp self`, which is a spin lock. */
    54  	for {
    55  		select {
    56  		case <-timeoutSem: //* Fulfill requested runtime.
    57  			return
    58  		default: //* Non-blocking.
    59  			continue
    60  		}
    61  	}
    62  }
    63  
    64  func (s *funcServer) Execute(ctx context.Context, req *proto.FaasRequest) (*proto.FaasReply, error) {
    65  	start := time.Now()
    66  	runtimeRequested := req.RuntimeInMilliSec
    67  	timeoutSem := time.After(time.Duration(runtimeRequested) * time.Millisecond)
    68  	if runtimeRequested <= 0 {
    69  		//* Some of the durations were incorrectly recorded as 0 in the trace.
    70  		return &proto.FaasReply{}, errors.New("non-positive execution time")
    71  	}
    72  
    73  	//* To avoid unecessary overheads, memory allocation is at the granularity of os pages.
    74  	delta := 2 //* Emperical skewness.
    75  	pageSize := unix.Getpagesize()
    76  	numPagesRequested := util.Mib2b(req.MemoryInMebiBytes) / uint32(pageSize) / uint32(delta)
    77  	bytes := make([]byte, numPagesRequested*uint32(pageSize))
    78  	timeout := false
    79  	for i := 0; i < int(numPagesRequested); i += pageSize {
    80  		select {
    81  		case <-timeoutSem:
    82  			timeout = true
    83  			goto finish //* Skip spin-lock.
    84  		default:
    85  			bytes[i] = byte(1) //* Materialise allocated memory.
    86  		}
    87  	}
    88  
    89  	busySpin(timeoutSem)
    90  
    91  finish:
    92  	var msg string
    93  	if msg = "Timed func -- OK"; timeout {
    94  		msg = "Timeout when materialising allocated memory."
    95  	}
    96  
    97  	return &proto.FaasReply{
    98  		Message:            msg,
    99  		DurationInMicroSec: uint32(time.Since(start).Microseconds()),
   100  		MemoryUsageInKb:    util.B2Kib(numPagesRequested * uint32(unix.Getpagesize())),
   101  	}, nil
   102  }
   103  
   104  func main() {
   105  	serverPort := 80 // For containers.
   106  	// 50051 for firecracker.
   107  	if len(os.Args) > 1 {
   108  		serverPort, _ = strconv.Atoi(os.Args[1])
   109  	}
   110  
   111  	lis, err := net.Listen("tcp", fmt.Sprintf(":%d", serverPort))
   112  	if err != nil {
   113  		log.Fatalf("failed to listen: %v", err)
   114  	}
   115  
   116  	funcServer := &funcServer{}
   117  	grpcServer := grpc.NewServer()
   118  	reflection.Register(grpcServer) // gRPC Server Reflection is used by gRPC CLI.
   119  	proto.RegisterExecutorServer(grpcServer, funcServer)
   120  	err = grpcServer.Serve(lis)
   121  	util.Check(err)
   122  }